/* SELLERLOGIC SCS - public Knowledge Base. Tokens + chrome mirror apps/www/src/assets/style.css. */
:root {
  --brand-300: #6b8fe0;
  --brand-400: #3868d5;
  --brand-500: #003ec9;
  --brand-600: #0032a1;
  --r: 12px;
  --r-lg: 18px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --measure: 1320px;

  color-scheme: light;
  --ink: 24 24 27;
  --surface: #ffffff;
  --lift: #f7f8fa;

  --txt: rgb(var(--ink) / 1);
  --txt-body: rgb(var(--ink) / 0.72);
  --txt-soft: rgb(var(--ink) / 0.68);
  --txt-sub: rgb(var(--ink) / 0.64);
  --txt-mute: rgb(var(--ink) / 0.6);
  --txt-cap: rgb(var(--ink) / 0.56);
  --txt-ghost: rgb(var(--ink) / 0.42);

  --line: rgb(var(--ink) / 0.1);
  --line-2: rgb(var(--ink) / 0.13);
  --line-3: rgb(var(--ink) / 0.18);
  --wash: rgb(var(--ink) / 0.035);
  --wash-faint: rgb(var(--ink) / 0.022);
  --hover: rgb(var(--ink) / 0.06);

  --chrome-bg: rgba(255, 255, 255, 0.78);
  --panel-bg: rgba(255, 255, 255, 0.96);
  --shadow-panel: rgb(24 24 27 / 0.45);
  --sel-bg: rgb(0 62 201 / 0.07);

  --accent: var(--brand-500);
  --accent-wash: rgb(0 62 201 / 0.07);
  --focus: var(--brand-500);

  /*
   * How tall the sticky header is - the offset both side rails come to rest at
   * (#1638). The bar alone is .bar's 88px plus the header's own hairline; a
   * page that also carries the breadcrumb row (#1590) adds that row on top,
   * which is what "body.has-crumb" below states. render.ts sets that class,
   * docs.js then measures the real element and overrides this value exactly,
   * so these two numbers are only the no-JavaScript floor. Erring high by a
   * pixel or two leaves a slightly larger gap; erring low hides the top of
   * both rails under the header, which is the bug this replaces.
   */
  --head-h: 89px;

  /*
   * Product design tokens for the rebuilt UI excerpts (kb-mockups.ts). NOT
   * written here: read at build time out of packages/ui/tailwind-preset.css by
   * src/ui-tokens.ts, so an excerpt is painted with the values the product
   * itself uses. Change a colour there and the manual follows on the next
   * build - which is the whole reason the excerpts exist instead of captures.
   */
  --mk-accent: #0052ff;
  --mk-accent-dark: #6699ff;
  --mk-paper: #ffffff;
  --mk-paper-2: #f5f5f7;
  --mk-edge: #e8e8ed;
  --mk-ink: #1d1d1f;
  --mk-ink-2: #4a4a52;
  --mk-ink-3: #515159;
  --mk-paper-dark: #111113;
  --mk-paper-2-dark: #18181b;
  --mk-ok: #248a3d;
  --mk-ok-dark: #30d158;
  --mk-warn: #b25000;
  --mk-warn-dark: #ffd60a;
  --mk-bad: #d70015;
  --mk-bad-dark: #ff453a;
  --mk-info: #0071a4;
  --mk-info-dark: #64d2ff;
  --mk-r: 12px;
  --mk-r-sm: 8px;
  --mk-font: "DM Sans", system-ui, sans-serif;
}

html.dark {
  color-scheme: dark;
  --ink: 255 255 255;
  --surface: #09090b;
  --lift: #0d0d11;

  /*
   * Text alphas: the same ladder the light palette above uses, so neither
   * appearance is the weaker one to read (#1551). White on #09090b is the
   * higher-contrast direction, so an equal alpha lands strictly above its
   * light-mode counterpart - 0.72 gives 10.27:1 here against 7.02:1 there.
   *
   * These sit above CLAUDE.md's HIG ladder (/60 secondary, /35 tertiary, /18
   * quaternary) on purpose. That ladder is calibrated for glanceable product
   * chrome; this is a long-form reading surface, and its /35 step was carrying
   * breadcrumbs, the sidebar section titles, EVERY table-of-contents entry and
   * the footer links at 3.13:1 - below WCAG AA. Where the two rules disagree,
   * AA wins: the operator set "mindestens WCAG AA" as the acceptance
   * criterion on 2026-08-12. __tests__/dark-contrast.test.ts holds the floor.
   */
  --txt-body: rgb(var(--ink) / 0.72);
  --txt-soft: rgb(var(--ink) / 0.68);
  --txt-sub: rgb(var(--ink) / 0.64);
  --txt-mute: rgb(var(--ink) / 0.6);
  --txt-cap: rgb(var(--ink) / 0.56);
  --txt-ghost: rgb(var(--ink) / 0.46);

  --line: rgb(var(--ink) / 0.07);
  --line-2: rgb(var(--ink) / 0.09);
  --line-3: rgb(var(--ink) / 0.14);
  --wash: rgb(var(--ink) / 0.03);
  --wash-faint: rgb(var(--ink) / 0.018);
  --hover: rgb(var(--ink) / 0.07);

  --chrome-bg: rgba(9, 9, 11, 0.72);
  --panel-bg: rgba(13, 13, 17, 0.96);
  --shadow-panel: rgb(0 0 0 / 0.7);
  --sel-bg: rgb(107 143 224 / 0.12);

  --accent: var(--brand-300);
  --accent-wash: rgb(107 143 224 / 0.12);
  --focus: var(--brand-300);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--txt-body);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  background: var(--surface); color: var(--txt); border: 1px solid var(--line-3);
  border-radius: var(--r); padding: .55rem .9rem; transition: top .15s var(--ease);
}
.skip:focus { top: 12px; }

/* ── Header (apps/www chrome, Knowledge-Base entries) ───────────────────── */
.wrap { max-width: var(--measure); margin: 0 auto; padding: 0 28px; }

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--chrome-bg);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--line);
}

/*
 * The search sits in the OPTICAL centre of the bar, not merely "after the
 * logo": from the tablet breakpoint up it is taken out of the flow and pinned
 * to the middle, so the lockup on the left and the controls on the right can
 * grow without pushing it off centre. Below 860px it drops into the flow as a
 * full-width row - see the responsive block.
 */
.bar {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  min-height: 88px;
}

.brand-group { display: flex; align-items: center; gap: 10px; min-width: 0; }

.logo { display: inline-flex; align-items: center; }

/*
 * The lockup, matched to sellerlogic.com: two files, one request, swapped by
 * "html.dark". Set as a background rather than two <img> elements because a
 * hidden <img> is still downloaded and the artwork is 19 kB. Both files are
 * written by apps/www/src/brand.ts - the same derivation the marketing site
 * uses, so the two headers can never drift apart.
 */
.logo-img {
  display: block;
  width: 230px;
  aspect-ratio: 184 / 40;
  background: url("/logo-light.svg") no-repeat center / contain;
}
html.dark .logo-img { background-image: url("/logo.svg"); }

/* The product designator beside the lockup - this site's own name. */
.kb-name {
  color: var(--txt); font-size: 14px; font-weight: 600; letter-spacing: -0.01em;
  padding: 6px 10px; border-radius: 99px; white-space: nowrap;
}
.kb-name:hover { background: var(--hover); text-decoration: none; }

nav.head-nav {
  order: 2; margin-left: auto;
  display: flex; align-items: center; gap: 1px; flex-wrap: wrap; justify-content: flex-end;
}
nav.head-nav > a, .menu summary {
  padding: 7px 12px; border-radius: 99px;
  font-size: 13px; font-weight: 500; color: var(--txt-body);
  transition: .18s var(--ease);
}
nav.head-nav > a:hover { background: var(--hover); color: var(--txt); text-decoration: none; }
nav.head-nav > a[aria-current] { color: var(--txt); background: var(--hover); }

/*
 * The header breadcrumb (#1590) - its own row under .bar, not inside it.
 * Only that keeps it visible once the burger collapses nav.head-nav below
 * 860px (that nav goes display:none until opened there - see the media
 * query further down - and a descendant of a display:none ancestor is gone
 * too). Omitted entirely on pages with no area (home, search, 404): see
 * render.ts's renderHeaderCrumb.
 */
.crumb-bar { border-top: 1px solid var(--line); }
.head-crumb {
  display: flex; align-items: center; gap: .35rem; flex-wrap: wrap;
  padding: .5rem 0; font-size: .82rem; color: var(--txt-cap);
}
.head-crumb a.crumb-area { color: var(--txt-cap); }
.head-crumb a.crumb-area:hover { color: var(--accent); text-decoration: none; }
.head-crumb .crumb-sep { color: var(--txt-ghost); }
.head-crumb [aria-current] { color: var(--txt); font-weight: 600; }
/*
 * The way home (#1638). Icon-only, so it gets a real hit area of its own
 * rather than the 13px the glyph occupies - this row is the navigation that
 * survives on a phone, where the target has to be thumb-sized.
 */
.head-crumb .crumb-home {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; margin-left: -6px;
  border-radius: 8px; color: var(--txt-cap);
}
.head-crumb .crumb-home:hover { background: var(--hover); color: var(--txt); text-decoration: none; }
.head-crumb .crumb-home .i-home { width: 16px; height: 16px; }
@media (max-width: 480px) {
  /* "shortened if need be (area only)" (operator brief) - the page title gives
     way first, the area segment never does. The house stays: it is the only
     way back to the start page at this width (the lockup leaves for the
     marketing site, ".kb-name" is gone below 1020px). Only the separator
     BETWEEN area and page title goes with the page title - addressed by
     position, because hiding every ".crumb-sep" would have taken the house's
     own separator too and glued the glyph to the area name. */
  .head-crumb .crumb-page, .head-crumb .crumb-area + .crumb-sep { display: none; }
}

/* Areas disclosure - apps/www's .menu, two columns, same panel treatment. */
.menu { position: relative; }
.menu summary { list-style: none; cursor: pointer; user-select: none; }
.menu summary::-webkit-details-marker { display: none; }
.menu summary::after {
  content: ""; display: inline-block; width: 5px; height: 5px;
  margin-left: 8px; vertical-align: 2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform .2s var(--ease);
}
.menu[open] summary::after { transform: rotate(-135deg); }
.menu summary:hover { background: var(--hover); color: var(--txt); }
/*
 * The open area, inside the panel rather than on the trigger (#1590 moved it
 * off the summary - the trigger reads "Produkte" everywhere now, see
 * render.ts's renderHeader). Same accent pair the sidebar's current entry
 * uses - one visual language for "current" - and the pair
 * __tests__/dark-contrast.test.ts already holds above WCAG AA in both
 * appearances.
 */
.menu-panel {
  position: absolute; top: calc(100% + 12px); right: -8px; left: auto;
  width: min(94vw, 720px); z-index: 60;
  background: var(--panel-bg); backdrop-filter: blur(20px);
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  padding: 8px; box-shadow: 0 30px 70px -30px var(--shadow-panel);
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px 24px; align-items: start;
  max-height: min(78vh, 660px); overflow-y: auto;
}
.menu-col { display: grid; gap: 2px; align-content: start; }
.menu-col + .menu-col { border-left: 1px solid var(--line-2); padding-left: 20px; }
.menu-panel a { display: grid; gap: 2px; padding: 11px 13px; border-radius: 10px; color: inherit; }
.menu-panel a strong { display: flex; align-items: baseline; gap: 7px; font-size: 14px; font-weight: 600; color: var(--txt); }
.menu-panel a strong i { font-style: normal; font-weight: 500; font-size: 12px; color: var(--txt-soft); }
.menu-panel a span { font-size: 12.5px; line-height: 1.4; color: var(--txt-mute); }
.menu-panel a:hover { background: var(--hover); text-decoration: none; }
.menu-panel a[aria-current] { background: var(--sel-bg); }
.menu-group { margin: 6px 13px 6px; font-size: 10.5px; letter-spacing: .13em; text-transform: uppercase; font-weight: 700; color: var(--txt-cap); }
.menu-all {
  grid-column: 1 / -1; margin-top: 8px;
  border-top: 1px solid var(--line); border-radius: 0 0 10px 10px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent); padding: 9px 13px;
}

/*
 * Language switcher (#1590) - same .menu disclosure as the areas menu
 * above, a narrower single-column panel instead of the two-column one. The
 * trigger keeps the small bordered-pill treatment the old direct link used
 * to carry.
 */
.lang-menu { margin-left: 8px; }
.lang-menu summary {
  border: 1px solid var(--line-3); font-size: 11.5px; font-weight: 600;
  padding: 5px 10px;
}
.lang-panel {
  position: absolute; top: calc(100% + 8px); right: -8px; left: auto;
  min-width: 9rem; max-width: min(80vw, 14rem); z-index: 60;
  background: var(--panel-bg); backdrop-filter: blur(20px);
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  padding: 6px; margin: 0; list-style: none;
  box-shadow: 0 30px 70px -30px var(--shadow-panel);
}
.lang-panel li + li { margin-top: 2px; }
.lang-panel a, .lang-panel .lang-current {
  display: flex; align-items: center; padding: 8px 11px; border-radius: 10px;
  font-size: 13px; color: inherit;
}
.lang-panel a:hover { background: var(--hover); text-decoration: none; }
.lang-panel .lang-current { background: var(--sel-bg); color: var(--accent); font-weight: 600; }

/* Appearance switch + burger: hidden until "html.js", exactly as on apps/www. */
.theme-t { display: none; }
.js .theme-t {
  order: 3;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-left: 8px; padding: 0;
  border: 1px solid var(--line-3); border-radius: 99px;
  background: transparent; color: var(--txt-body); cursor: pointer;
  transition: .18s var(--ease);
}
.js .theme-t:hover { background: var(--hover); color: var(--txt); }
.theme-t svg { display: block; width: 16px; height: 16px; }
.theme-t .i-sun { display: none; }
html.dark .theme-t .i-sun { display: block; }
html.dark .theme-t .i-moon { display: none; }

.nav-t { display: none; }
.nav-t .i-close { display: none; }
.nav-t[aria-expanded="true"] svg:not(.i-close) { display: none; }
.nav-t[aria-expanded="true"] .i-close { display: block; }

/* ── Search (centred in the bar) ─────────────────────────────────────────── */
.search { position: relative; width: 100%; min-width: 0; }
@media (min-width: 861px) {
  .site-header .search {
    position: absolute; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: min(30rem, 40vw);
  }
}
.search input {
  width: 100%; min-height: 38px;
  padding: .35rem .8rem .35rem 2.1rem;
  border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--wash); color: var(--txt);
  font: inherit; font-size: .9rem;
}
.search input:hover { border-color: var(--line-3); }
.search input::placeholder { color: var(--txt-ghost); }
.search .search-icon {
  position: absolute; left: .72rem; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--txt-ghost); pointer-events: none;
}

/* The landing hero's large field - the portal's own home search. */
.search-hero { max-width: 36rem; margin: 0 auto; }
.search-hero input { min-height: 48px; font-size: 1rem; padding-left: 2.6rem; }
.search-hero .search-icon { left: .95rem; width: 19px; height: 19px; }

/* ── Search results (portal presentation: a list, not a dropdown) ────────── */
.search-results { display: flex; flex-direction: column; gap: 1rem; }
.search-head { border-bottom: 1px solid var(--line); padding-bottom: .75rem; }
.search-head h2 { margin: 0; font-size: 1.15rem; color: var(--txt); font-weight: 650; }
.search-head p { margin: .2rem 0 0; color: var(--txt-cap); font-size: .88rem; }
.hit-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.search-hit {
  display: flex; flex-direction: column; gap: .35rem;
  padding: .7rem .9rem; border: 1px solid var(--line-2); border-radius: var(--r);
  color: inherit; transition: border-color .15s var(--ease), background .15s var(--ease);
}
.search-hit:hover, .search-hit[aria-selected="true"] { border-color: var(--accent); background: var(--wash-faint); text-decoration: none; }
.search-hit .hit-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.search-hit .hit-area {
  border-radius: 999px; background: var(--wash); color: var(--txt-cap);
  padding: .05rem .5rem; font-size: .7rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
}
.search-hit .hit-title { color: var(--txt); font-weight: 600; font-size: .95rem; }
.search-hit .hit-snippet { color: var(--txt-sub); font-size: .87rem; line-height: 1.5; }
.search-hit mark { background: var(--accent-wash); color: var(--txt); border-radius: 3px; padding: 0 .12em; }
.search-empty { color: var(--txt-cap); font-size: .9rem; }
[hidden] { display: none !important; }

/* ── Layout ─────────────────────────────────────────────────────────────── */
.layout {
  display: grid; gap: clamp(1rem, 3vw, 2.5rem);
  grid-template-columns: 15rem minmax(0, 1fr);
  max-width: 84rem; margin: 0 auto;
  padding: 1.25rem clamp(.9rem, 3vw, 1.75rem) 5rem;
}
.layout.has-toc { grid-template-columns: 15rem minmax(0, 1fr) 14rem; }
.layout.wide { grid-template-columns: minmax(0, 1fr); max-width: 76rem; }

/* Pages carrying the breadcrumb row under the bar - see --head-h in :root.
   134px measured in Chromium at 1440x900: the 89px bar plus a 45px crumb row
   (16px padding, a 28px hit target for the house, the hairline). */
body.has-crumb { --head-h: 134px; }

/*
 * Both side rails stay put while the article scrolls (#1638).
 *
 * Three properties, and all three are load-bearing:
 *
 *  - "top" clears the WHOLE header, breadcrumb row included. It used to be a
 *    flat 6.2rem, i.e. the bar's height before that row existed, so the top of
 *    each rail scrolled under the header and stopped there.
 *  - "max-height" keeps a rail inside the free height between header and
 *    viewport bottom. Without it a rail longer than the screen - the left one
 *    is, with an area expanded - would simply extend past the bottom edge and
 *    its last entries would be unreachable, which is the classic way a fixed
 *    navigation breaks.
 *  - "overflow-y: auto" is what then makes that rail scroll IN ITSELF, and
 *    "overscroll-behavior: contain" stops that scroll from continuing into the
 *    page once the rail hits its end.
 *
 * "dvh" is stated after "vh" rather than instead of it: a browser that does not
 * know the unit keeps the "vh" line, so the max-height can never be lost
 * entirely and take the internal scrolling with it.
 */
.sidebar { min-width: 0; }
.sidebar-inner, .toc-inner {
  position: sticky;
  top: calc(var(--head-h) + 1rem);
  max-height: calc(100vh - var(--head-h) - 2rem);
  max-height: calc(100dvh - var(--head-h) - 2rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.sidebar-inner { padding-right: .4rem; }
.nav-group + .nav-group { margin-top: 1.25rem; }
.nav-title {
  margin: 0 0 .4rem; padding-left: .55rem;
  color: var(--txt-cap); font-size: .72rem; font-weight: 640;
  text-transform: uppercase; letter-spacing: .07em;
}
.nav-list { list-style: none; margin: 0; padding: 0; }
.nav-list a {
  display: block; padding: .3rem .55rem; border-radius: 8px;
  color: var(--txt-sub); font-size: .9rem;
}
.nav-list a:hover { background: var(--hover); color: var(--txt); text-decoration: none; }
.nav-list a[aria-current] { background: var(--accent-wash); color: var(--accent); font-weight: 600; }
.nav-list .nav-sub { list-style: none; margin: .2rem 0 .5rem; padding: 0 0 0 .7rem; border-left: 1px solid var(--line); }
.nav-list .nav-sub a { font-size: .87rem; }

/*
 * Area disclosure (#1551): the link keeps the row, the button beside it opens
 * and closes the subtree. Without JavaScript the button is inert and the
 * subtree stays open, which is what this sidebar always did.
 */
.nav-row { display: flex; align-items: center; gap: 2px; }
.nav-row > a { flex: 1 1 auto; min-width: 0; }
.nav-exp {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0; border: 0; border-radius: 8px;
  background: transparent; color: var(--txt-cap); cursor: pointer;
  transition: transform .18s var(--ease), background .18s var(--ease), color .18s var(--ease);
}
.nav-exp:hover { background: var(--hover); color: var(--txt); }
.nav-exp .chev { width: 15px; height: 15px; transition: transform .18s var(--ease); }
.nav-exp[aria-expanded="false"] .chev { transform: rotate(-90deg); }
.nav-sub[hidden] { display: none; }

/*
 * Explicit grid placement (#2273).
 *
 * The rail now comes AFTER <main> in source order, so that a reader on a phone
 * - and a crawler, and a text browser - meets the article before 68 navigation
 * links rather than after them. Measured before this change on /de/crm/anrufe/
 * at 390px: the rail occupied y=178..2511 and <main> started at y=2527.
 *
 * Source order and visual order are separate things in a grid, so the desktop
 * layout is unchanged: the rail is placed in the first column by name, not by
 * being written first.
 */
.layout > .sidebar { grid-column: 1; grid-row: 1; }
.layout > .content { grid-column: 2; grid-row: 1; }
.layout > .toc { grid-column: 3; grid-row: 1; }
.layout.wide > .content { grid-column: 1; }
.layout > .rail-t { grid-column: 1; grid-row: 1; display: none; }

/*
 * The rail's filter field (#2273). Shown only with JavaScript, because without
 * it the field could not filter anything and an inert search box is worse than
 * no search box.
 */
.nav-filter { display: none; margin-bottom: .7rem; }
.js .nav-filter { display: block; }
.nav-filter input {
  box-sizing: border-box; width: 100%; min-height: 36px;
  padding: .35rem .6rem; border: 1px solid var(--line-2); border-radius: 8px;
  background: var(--bg); color: var(--txt); font: inherit; font-size: .88rem;
}
.nav-filter input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.nav-filter-empty { margin: .5rem .55rem 0; color: var(--txt-cap); font-size: .84rem; }

/* ── Content ────────────────────────────────────────────────────────────── */
.content { min-width: 0; }
.breadcrumbs { font-size: .82rem; color: var(--txt-cap); margin-bottom: 1rem; }
.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .3rem; margin: 0; padding: 0; }
.breadcrumbs li::after { content: "/"; margin-left: .3rem; color: var(--txt-ghost); }
.breadcrumbs li:last-child::after { content: ""; }
.breadcrumbs a { color: var(--txt-cap); }
.breadcrumbs a:hover { color: var(--accent); }

.prose { max-width: 46rem; }
.prose.full { max-width: none; }
.prose h1 {
  margin: 0 0 .75rem; color: var(--txt);
  font-size: clamp(1.75rem, 1.3rem + 1.6vw, 2.35rem);
  line-height: 1.15; letter-spacing: -0.022em; font-weight: 680;
}
.prose h2 {
  margin: 2.6rem 0 .7rem; color: var(--txt);
  font-size: clamp(1.2rem, 1.05rem + .55vw, 1.45rem);
  line-height: 1.25; letter-spacing: -0.014em; font-weight: 650;
  padding-top: 1.1rem; border-top: 1px solid var(--line);
}
.prose h3 { margin: 1.9rem 0 .5rem; color: var(--txt); font-size: 1.08rem; font-weight: 640; letter-spacing: -0.008em; }
.prose h4 { margin: 1.4rem 0 .4rem; color: var(--txt); font-size: .97rem; font-weight: 640; }
.prose p, .prose li { color: var(--txt-body); }
.prose p { margin: 0 0 1rem; }
.prose ul, .prose ol { margin: 0 0 1rem; padding-left: 1.3rem; }
.prose li { margin: .3rem 0; }
.prose li::marker { color: var(--txt-ghost); }
.prose strong { color: var(--txt); font-weight: 640; }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.2rem 0; }
.prose blockquote {
  margin: 1.2rem 0; padding: .1rem 1rem;
  border-left: 3px solid var(--line-3); color: var(--txt-sub);
}
.prose .lede { font-size: 1.06rem; color: var(--txt-sub); }

.prose table { width: 100%; border-collapse: collapse; margin: 1.2rem 0; font-size: .92rem; }
.prose th, .prose td { border: 1px solid var(--line-2); padding: .5rem .65rem; text-align: left; vertical-align: top; }
.prose th { background: var(--wash); color: var(--txt); font-weight: 620; }
.table-scroll { overflow-x: auto; }

.prose code {
  background: var(--wash); border: 1px solid var(--line); border-radius: 5px;
  padding: .08em .32em; font-size: .88em;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}
.prose pre {
  background: var(--lift); border: 1px solid var(--line); border-radius: var(--r);
  padding: .9rem 1rem; overflow-x: auto; margin: 1.2rem 0;
}
.prose pre code { background: none; border: 0; padding: 0; font-size: .86rem; }
.prose pre.mermaid { color: var(--txt-sub); }

.prose img { max-width: 100%; height: auto; border-radius: var(--r); }

/* Screenshot placeholder: a real slot on the page, not a silent gap. */
figure.kb-shot { margin: 1.4rem 0; }
.kb-shot .shot-frame {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 16 / 9; border: 1px dashed var(--line-3); border-radius: var(--r-lg);
  background: repeating-linear-gradient(45deg, var(--wash-faint) 0 10px, transparent 10px 20px);
  color: var(--txt-ghost);
}
.kb-shot .shot-frame svg { width: 34px; height: 34px; }
.kb-shot figcaption { margin-top: .45rem; color: var(--txt-cap); font-size: .8rem; }
.kb-shot figcaption code { font-size: .95em; }

/* Resolved screenshot: the same slot, now carrying the real capture. The
   width/height attributes reserve the box, so nothing jumps when it loads. */
.kb-shot .shot-img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line-3); border-radius: var(--r-lg);
}
/* Exactly one variant is visible per theme. The hidden one stays display:none
   AND loading=lazy, so the browser never downloads it. The has-dark class guards
   the swap: a capture without a dark twin keeps showing its light image. */
.kb-shot .shot-img-dark { display: none; }
html.dark .kb-shot.has-dark .shot-img { display: none; }
html.dark .kb-shot.has-dark .shot-img-dark { display: block; }

/* ── Rebuilt UI excerpt (#1610) ──────────────────────────────────────────────
   The successor to the screenshot: the described excerpt, drawn from the
   product's own wording (packages/i18n) and its own tokens (--mk-*, read out
   of packages/ui/tailwind-preset.css by src/ui-tokens.ts).

   Two appearances, not one - that is the point a capture could never make: the
   role tokens below are re-pointed under html.dark, so the excerpt follows the
   reader instead of freezing whichever theme the capture robot happened to run
   in.

   Nothing inside is an <a>, <button> or <input>, and nothing scrolls: a
   scrollable box would become tab-reachable in Chrome, which the acceptance
   criteria of #1610 rule out. Wide tables therefore SHRINK (see the media
   query at the end) rather than scroll. */
figure.kb-mock { margin: 1.4rem 0; }
.kb-mock figcaption { margin-top: .45rem; color: var(--txt-cap); font-size: .8rem; }
.kb-mock figcaption code { font-size: .95em; }

.mk {
  /* Light appearance: the admin's own light palette. */
  --s: var(--mk-paper);
  --s2: var(--mk-paper-2);
  --e: var(--mk-edge);
  --i: var(--mk-ink);
  --i2: var(--mk-ink-2);
  --i3: var(--mk-ink-3);
  --a: var(--mk-accent);
  --c-ok: var(--mk-ok);
  --c-warn: var(--mk-warn);
  --c-bad: var(--mk-bad);
  --c-info: var(--mk-info);

  position: relative;
  background: var(--s);
  border: 1px solid var(--line-3);
  border-radius: var(--r-lg);
  padding: 1.15rem 1.15rem 1.25rem;
  font-family: var(--mk-font);
  font-size: .875rem;
  line-height: 1.45;
  color: var(--i);
  overflow: hidden;
}
/*
 * Dark appearance per docs/DESIGN-SYSTEM.md: white at an alpha, never a
 * surface-* text token. The caption level is deliberately lifted from the
 * admin's text-white/35 to /50 - in the product that ramp sits next to a live
 * screen the reader can enlarge, here it IS the documentation and has to clear
 * WCAG AA on its own (asserted in __tests__/mockup-contrast.test.ts).
 */
html.dark .mk {
  --s: var(--mk-paper-dark);
  --s2: var(--mk-paper-2-dark);
  --e: rgb(255 255 255 / 0.08);
  --i: rgb(255 255 255 / 1);
  --i2: rgb(255 255 255 / 0.6);
  --i3: rgb(255 255 255 / 0.5);
  --a: var(--mk-accent-dark);
  --c-ok: var(--mk-ok-dark);
  --c-warn: var(--mk-warn-dark);
  --c-bad: var(--mk-bad-dark);
  --c-info: var(--mk-info-dark);
}

/* "Abbildung" - the excerpt looks operable, so it says in words that it is not. */
.mk-flag {
  position: absolute; top: .55rem; right: .6rem;
  font-size: .62rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--i3); border: 1px solid var(--e); border-radius: 999px;
  padding: .1rem .45rem; background: var(--s2);
}
.mk-head { margin: 0 5rem .8rem 0; }
.mk-title { font-weight: 650; font-size: 1rem; color: var(--i); }
.mk-body { display: flex; flex-direction: column; gap: .85rem; }

/* Tiles */
.mk-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: .6rem; }
.mk-tile { border: 1px solid var(--e); border-radius: var(--mk-r-sm); padding: .6rem .7rem; background: var(--s2); }
.mk-tile-label { color: var(--i2); font-size: .74rem; }
.mk-tile-value { color: var(--i); font-size: 1.35rem; font-weight: 620; line-height: 1.2; margin-top: .1rem; }
.mk-tile-note { color: var(--i3); font-size: .72rem; margin-top: .15rem; }

/* Table */
.mk-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
/* border:0 first: the excerpt sits inside .prose, whose own table rule draws a
   full grid. The admin's tables have horizontal rules only. */
.mk-table th, .mk-table td {
  text-align: left; padding: .45rem .55rem; border: 0; border-bottom: 1px solid var(--e);
  overflow-wrap: break-word; vertical-align: top;
}
.mk-table th { color: var(--i2); font-weight: 600; font-size: .74rem; letter-spacing: .02em; background: var(--s2); }
.mk-table td { color: var(--i); }
.mk-table tr:last-child td { border-bottom: 0; }

/* Form */
.mk-form { display: flex; flex-direction: column; gap: .6rem; }
.mk-label { color: var(--i2); font-size: .74rem; margin-bottom: .2rem; }
.mk-input {
  border: 1px solid var(--e); border-radius: var(--mk-r-sm); background: var(--s2);
  padding: .45rem .6rem; color: var(--i); display: flex; justify-content: space-between; gap: .5rem;
}
.mk-chevron { color: var(--i3); }
.mk-hint { color: var(--i3); font-size: .72rem; margin-top: .2rem; }
.mk-switch-row { display: flex; align-items: center; gap: .55rem; flex-wrap: wrap; }
.mk-switch-row .mk-label { margin: 0; flex: 1 1 auto; }
.mk-switch { width: 2.1rem; height: 1.2rem; border-radius: 999px; background: var(--e); position: relative; flex: none; }
.mk-switch.is-on { background: var(--a); }
.mk-knob {
  position: absolute; top: .15rem; left: .15rem; width: .9rem; height: .9rem;
  border-radius: 999px; background: var(--mk-paper);
}
.mk-switch.is-on .mk-knob { left: auto; right: .15rem; }
/* The word next to the switch - the state must not rest on the colour alone. */
.mk-switch-state { color: var(--i2); font-size: .74rem; }

/* Definition pairs */
.mk-defs { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .55rem .9rem; margin: 0; }
.mk-def dt { color: var(--i2); font-size: .74rem; }
.mk-def dd { color: var(--i); margin: .1rem 0 0; }

/* List rows */
.mk-list { border: 1px solid var(--e); border-radius: var(--mk-r-sm); overflow: hidden; }
.mk-row {
  display: flex; align-items: center; gap: .6rem; padding: .5rem .65rem;
  border-bottom: 1px solid var(--e); background: var(--s);
}
.mk-row:last-child { border-bottom: 0; }
.mk-row-main { flex: 1 1 auto; min-width: 0; }
.mk-row-title { color: var(--i); }
.mk-row-sub { color: var(--i3); font-size: .74rem; }
.mk-row-state { flex: none; color: var(--i2); font-size: .78rem; }

/* Tabs */
.mk-tabs { display: flex; flex-wrap: wrap; gap: .2rem; border-bottom: 1px solid var(--e); }
.mk-tab { padding: .35rem .6rem; color: var(--i2); font-size: .8rem; border-bottom: 2px solid transparent; }
/* Bold + underline carry the selection as well as the colour does. */
.mk-tab.is-active { color: var(--a); border-bottom-color: var(--a); font-weight: 620; }

/* Notice */
.mk-note {
  display: flex; gap: .5rem; align-items: flex-start; padding: .55rem .7rem;
  border: 1px solid var(--e); border-left: 3px solid currentColor; border-radius: var(--mk-r-sm);
  background: var(--s2); color: var(--i2);
}
.mk-note > span:last-child { color: var(--i); }
.mk-note.mk-ok { color: var(--c-ok); }
.mk-note.mk-warn { color: var(--c-warn); }
.mk-note.mk-bad { color: var(--c-bad); }
.mk-note.mk-info { color: var(--c-info); }

/* Steps */
.mk-steps { display: flex; flex-wrap: wrap; gap: .5rem 1rem; }
.mk-step { display: flex; align-items: center; gap: .4rem; color: var(--i2); font-size: .8rem; }
.mk-step-dot {
  width: 1.25rem; height: 1.25rem; border-radius: 999px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--e); background: var(--s2); font-size: .7rem; color: var(--i2);
}
.mk-step.is-erledigt .mk-step-dot { background: var(--a); border-color: var(--a); color: var(--mk-paper); }
html.dark .mk-step.is-erledigt .mk-step-dot { color: var(--mk-paper-dark); }
.mk-step.is-aktiv .mk-step-dot { border-color: var(--a); color: var(--a); }
.mk-step.is-aktiv .mk-step-label { color: var(--i); font-weight: 600; }

/* Buttons - drawn, never pressable. */
.mk-actions { display: flex; flex-wrap: wrap; gap: .45rem; }
.mk-action {
  border: 1px solid var(--e); border-radius: var(--mk-r-sm); padding: .35rem .75rem;
  color: var(--i2); background: var(--s2); font-size: .8rem;
}
/* Label on the accent fill: paper-on-brand in light, ink-on-brand-300 in dark.
   Swapped rather than kept white, because white on brand-300 would be 2.8:1. */
.mk-action.is-primary { background: var(--a); border-color: var(--a); color: var(--mk-paper); font-weight: 600; }
html.dark .mk-action.is-primary { color: var(--mk-paper-dark); }

/* Status chip. The word stays at full text contrast; the glyph and the outline
   carry the state redundantly, so neither colour nor shape stands alone. */
.mk-badge {
  display: inline-flex; align-items: center; gap: .3rem;
  border: 1px solid var(--e); border-radius: 999px; padding: .05rem .45rem;
  background: var(--s2); color: var(--i); font-size: .76rem; white-space: nowrap;
}
.mk-glyph { font-weight: 700; }
.mk-badge.mk-ok { border-color: var(--c-ok); } .mk-badge.mk-ok .mk-glyph { color: var(--c-ok); }
.mk-badge.mk-warn { border-color: var(--c-warn); } .mk-badge.mk-warn .mk-glyph { color: var(--c-warn); }
.mk-badge.mk-bad { border-color: var(--c-bad); } .mk-badge.mk-bad .mk-glyph { color: var(--c-bad); }
.mk-badge.mk-info { border-color: var(--c-info); } .mk-badge.mk-info .mk-glyph { color: var(--c-info); }
.mk-badge.mk-neutral .mk-glyph { color: var(--i3); }

/* 640px, not the chrome's 620px: the excerpt's own breakpoint must stay a
   separate block, or a regex that grabs "the 620px block" (see
   __tests__/sticky-rails-and-cta.test.ts, #1651) picks up this one instead of
   the header's. */
@media (max-width: 640px) {
  .mk { font-size: .8rem; padding: 1rem .75rem 1.1rem; }
  .mk-table th, .mk-table td { padding: .35rem .3rem; }
  .mk-tile-value { font-size: 1.15rem; }
}

/* ── Home (portal parity: hero → what's new → products → cross-cutting) ─── */
.home { display: flex; flex-direction: column; gap: 2rem; }
.hero {
  display: flex; flex-direction: column; align-items: center; gap: 1rem;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--wash-faint);
  padding: 2.5rem clamp(1rem, 4vw, 2rem); text-align: center;
}
.hero h1 {
  margin: 0; max-width: 40rem; color: var(--txt);
  font-size: clamp(1.4rem, 1.1rem + 1.2vw, 1.9rem);
  line-height: 1.25; letter-spacing: -0.018em; font-weight: 680;
}
/*
 * The tagline, below the heading instead of AS the heading (#SEO-B, 2026-08-17).
 * It reads as a sentence about the site, not as its name, and at 102/99
 * characters it was the only <h1> on the site over 70 - the one heading a
 * reader cannot scan. The <h1> now names the site; this keeps every word of the
 * tagline, one step down in weight and colour.
 */
.hero-lede {
  margin: 0; max-width: 46rem; color: var(--txt-soft);
  font-size: clamp(.95rem, .9rem + .3vw, 1.05rem);
  line-height: 1.5;
}
.section { display: flex; flex-direction: column; gap: .75rem; }
.section-title {
  margin: 0; color: var(--txt-cap);
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
}

.hub-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); }
.hub {
  display: flex; flex-direction: column; gap: .5rem; height: 100%;
  padding: 1rem; border: 1px solid var(--line-2); border-radius: var(--r-lg);
  background: var(--wash-faint); color: inherit;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.hub:hover { border-color: var(--accent); background: var(--wash); text-decoration: none; }
.hub-head { display: flex; align-items: center; gap: .6rem; }
.hub-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 10px;
  background: var(--wash); color: var(--txt-soft);
}
.hub-icon svg { width: 19px; height: 19px; }
.hub-name { color: var(--txt); font-weight: 620; letter-spacing: -0.01em; }
.hub-desc { margin: 0; color: var(--txt-sub); font-size: .88rem; line-height: 1.5; }
.hub-count { margin: auto 0 0; padding-top: .2rem; color: var(--txt-cap); font-size: .78rem; }

/* ── Release notes (portal parity: month → area → entry cards) ───────────── */
.release-feed { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.release-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.release-item {
  display: flex; flex-direction: column; gap: .3rem;
  padding: .75rem .9rem; border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--wash-faint);
}
.release-item .rel-meta { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; flex-wrap: wrap; }
.release-item .rel-area {
  border-radius: 999px; background: var(--wash); color: var(--txt-cap);
  padding: .05rem .5rem; font-size: .7rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
}
.release-item time { color: var(--txt-cap); font-size: .76rem; white-space: nowrap; }
.release-item .rel-title { margin: 0; color: var(--txt); font-weight: 620; }
.release-item .rel-text { margin: 0; color: var(--txt-sub); font-size: .89rem; line-height: 1.55; }
.release-item .rel-link { font-size: .86rem; }
/*
 * The home feed is a teaser of three, side by side - one long entry there would
 * push the area tiles off the first screen (the portal's feed sits in a narrow
 * column and never grows this tall). Four lines, then the full text one click
 * away under "Neuerungen". The release-notes PAGE itself clamps nothing.
 */
.release-feed .rel-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}

.release-month { margin: 0 0 2.5rem; }
.release-month > h2 { margin-top: 0; border-top: 0; padding-top: 0; }
.release-area-title { margin: 1.2rem 0 .5rem; font-size: .95rem; font-weight: 650; color: var(--txt); }
.month-badge {
  display: inline-block; margin-bottom: .6rem; padding: .12rem .55rem;
  border: 1px solid var(--line-2); border-radius: 999px;
  background: var(--wash); color: var(--txt-cap);
  font-size: .74rem; font-weight: 620; letter-spacing: .04em;
}
.month-chips { list-style: none; display: flex; flex-wrap: wrap; gap: .4rem; margin: 1rem 0 2rem; padding: 0; }
.month-chips a {
  display: inline-block; padding: .2rem .7rem;
  border: 1px solid var(--line-2); border-radius: 999px;
  color: var(--txt-sub); font-size: .82rem; text-transform: capitalize;
}
.month-chips a:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

/* ── Cards / lists ──────────────────────────────────────────────────────── */
.card-grid {
  list-style: none; margin: 1.4rem 0 0; padding: 0;
  display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.card {
  display: block; height: 100%; padding: .9rem 1rem;
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  background: var(--wash-faint); color: inherit;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.card:hover { border-color: var(--accent); background: var(--wash); text-decoration: none; }
.card .card-title { display: block; color: var(--txt); font-weight: 620; letter-spacing: -0.01em; }
.card .card-sub { display: block; margin-top: .2rem; color: var(--txt-cap); font-size: .85rem; }

.page-list { list-style: none; margin: 1.2rem 0 0; padding: 0; }
.page-list li { border-bottom: 1px solid var(--line); }
.page-list a { display: block; padding: .65rem .2rem; color: var(--txt); }
.page-list a:hover { color: var(--accent); text-decoration: none; }

/* ── Table of contents ──────────────────────────────────────────────────── */
/* The sticky/overflow behaviour of .toc-inner is declared with .sidebar-inner
   in the layout block above - one rule, so the two rails cannot drift apart. */
.toc { min-width: 0; }
.toc-list { list-style: none; margin: 0; padding: 0; border-left: 1px solid var(--line); }
.toc-list a { display: block; padding: .22rem .6rem; color: var(--txt-cap); font-size: .84rem; line-height: 1.4; }
.toc-list a:hover { color: var(--accent); text-decoration: none; }
.toc-list .lvl-3 a { padding-left: 1.3rem; font-size: .8rem; }

/*
 * ── Sign-up hint under the contents (#1638) ──────────────────────────────
 * A help, not an advertising slot: a hairline box, body-sized text, one plain
 * link. No gradient (docs/DESIGN-SYSTEM.md), no colour doing the talking - the
 * heading, the sentence and the link each say what they mean in words, so the
 * box reads the same to somebody who cannot tell the accent from the body
 * text. Every colour is a token, so light and dark keep the same ladder.
 * It follows the contents inside .toc-inner and therefore scrolls with the
 * rail rather than sitting on top of it.
 */
.cta {
  margin-top: 1.75rem; padding: .9rem 1rem;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--wash-faint);
}
.cta-title { margin: 0 0 .35rem; color: var(--txt); font-size: .86rem; font-weight: 640; }
/*
 * --txt-sub, not the quieter --txt-cap the neighbouring contents list uses.
 * At 0.56 alpha that token lands at 4.08:1 on the light page - below WCAG AA,
 * which __tests__/dark-contrast.test.ts never caught because it holds the
 * floor for the DARK palette only (white ink on near-black is the
 * higher-contrast direction, so the same alpha clears AA there and fails
 * here). 0.64 gives 5.31:1 light and 8.29:1 dark. One step below the body
 * text is quiet enough; below AA is not quiet, it is unreadable.
 */
.cta-text { margin: 0 0 .7rem; color: var(--txt-sub); font-size: .8rem; line-height: 1.5; }
.cta-link {
  display: inline-block; padding: .35rem .8rem;
  border: 1px solid var(--accent); border-radius: 999px;
  color: var(--accent); font-size: .8rem; font-weight: 600;
}
.cta-link:hover { background: var(--accent-wash); text-decoration: none; }

/* ── Footer (apps/www: legal line + imprint) ─────────────────────────────── */
.site-footer { border-top: 1px solid var(--line); margin-top: 2rem; }
.foot {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: .75rem 1.5rem; padding-top: 1.6rem; padding-bottom: 1.6rem;
}
.foot-legal { margin: 0; color: var(--txt-cap); font-size: .84rem; line-height: 1.6; }
.foot-legal .cap { color: var(--txt-ghost); }
.foot-nav { margin-left: auto; display: flex; flex-wrap: wrap; gap: .25rem 1rem; }
.foot-nav a { color: var(--txt-cap); font-size: .84rem; }
.foot-nav a:hover { color: var(--accent); }

/* ── Responsive: single column from tablet down, usable at 390px ────────── */
@media (max-width: 1180px) {
  .layout, .layout.has-toc { grid-template-columns: 15rem minmax(0, 1fr); }
  .toc { display: none; }
}
@media (max-width: 1020px) {
  /* The designator gives way first so the centred field keeps its width. */
  .kb-name { display: none; }
}
@media (max-width: 860px) {
  .layout, .layout.has-toc, .layout.wide { grid-template-columns: minmax(0, 1fr); }
  .layout > .sidebar, .layout > .content, .layout > .toc, .layout > .rail-t { grid-column: 1; grid-row: auto; }
  .sidebar-inner { position: static; max-height: none; overflow: visible; }

  /*
   * The narrow-screen rail (#2273), in two states.
   *
   * WITHOUT JavaScript nothing below applies except the trigger staying
   * hidden: the rail is the inline <details> menu it always was, now sitting
   * under the article instead of on top of it. Long, but complete, crawlable
   * and reachable - which is what the ticket asks the no-script state to be.
   *
   * WITH JavaScript the rail becomes an off-canvas drawer and the trigger
   * appears above the article. The drawer is fixed-positioned, so opening it
   * does not push the content anywhere; closing it leaves the article exactly
   * where the reader was.
   */
  .js .layout > .rail-t {
    display: inline-flex; align-items: center; gap: .5rem;
    align-self: start; margin-bottom: .25rem;
    min-height: 44px; padding: .45rem .8rem;
    border: 1px solid var(--line-3); border-radius: 99px;
    background: transparent; color: var(--txt-body); font: inherit; font-size: .9rem; cursor: pointer;
  }
  .js .rail-t svg { display: block; width: 20px; height: 20px; }
  .js .rail-t .i-close { display: none; }
  .js .rail-t[aria-expanded="true"] .i-close { display: block; }
  .js .rail-t[aria-expanded="true"] svg:not(.i-close) { display: none; }

  .js .layout > .sidebar {
    position: fixed; z-index: 60; inset: 0 auto 0 0;
    width: min(20rem, 86vw); max-width: 86vw;
    padding: 1rem .9rem 1.5rem;
    background: var(--bg); border-right: 1px solid var(--line-2);
    box-shadow: 0 0 40px rgba(0,0,0,.28);
    overflow-y: auto; overscroll-behavior: contain;
    transform: translateX(-102%); visibility: hidden;
    /*
     * The delay on "visibility" is load-bearing, not decoration. Transitioned
     * WITH a duration, visibility stays "hidden" for the first frame after the
     * class lands - and a browser refuses focus() on a hidden element, so the
     * drawer opened with focus left behind on the trigger and the focus trap
     * had nothing to hold. Zero duration plus a delay flips it instantly on
     * open and only after the slide-out on close, which is what both states
     * actually need.
     */
    transition: transform .22s var(--ease), visibility 0s linear .22s;
  }
  .js .layout > .sidebar.rail-open {
    transform: translateX(0); visibility: visible;
    transition: transform .22s var(--ease), visibility 0s linear 0s;
  }
  .js .rail-scrim {
    position: fixed; inset: 0; z-index: 59;
    background: rgba(0,0,0,.42); border: 0; padding: 0;
    opacity: 0; visibility: hidden; transition: opacity .22s var(--ease), visibility 0s linear .22s;
  }
  .js .rail-scrim.rail-open {
    opacity: 1; visibility: visible;
    transition: opacity .22s var(--ease), visibility 0s linear 0s;
  }
  /* The disclosure summary is the trigger's job here - two of them would be two
     ways to do one thing, and the drawer already carries a close control. */
  .js .layout > .sidebar details > summary { display: none; }
  .js body.rail-locked { overflow: hidden; }
  .sidebar details > summary {
    list-style: none; cursor: pointer;
    display: flex; align-items: center; justify-content: space-between;
    padding: .55rem .8rem; border: 1px solid var(--line-2); border-radius: var(--r);
    color: var(--txt); font-size: .9rem; font-weight: 600;
  }
  .sidebar details > summary::-webkit-details-marker { display: none; }
  .sidebar details[open] > summary { margin-bottom: .6rem; }

  /*
   * The marketing site's phone bar, entry for entry: logo + burger + switch on
   * the top row, everything else beneath it. Here the search takes the second
   * row (it is the primary control of a Knowledge Base) and the nav, when
   * opened, the third.
   */
  .bar { flex-wrap: wrap; padding-block: 12px; }
  .brand-group { order: 0; }
  .logo-img { width: min(230px, 44vw); }
  .js .nav-t {
    order: 1; display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin-left: auto; padding: 0;
    border: 1px solid var(--line-3); border-radius: 99px;
    background: transparent; color: var(--txt-body); cursor: pointer;
    transition: .18s var(--ease);
  }
  .js .nav-t:hover { background: var(--hover); color: var(--txt); }
  .js .nav-t svg { display: block; width: 20px; height: 20px; }
  .js .nav-t .i-close, .js .nav-t[aria-expanded="true"] svg:not(.i-close) { display: none; }
  .js .nav-t[aria-expanded="true"] .i-close { display: block; }
  .js .theme-t { order: 2; margin-left: 8px; width: 44px; height: 44px; }
  /*
   * No margin-top (#1651). ".bar" is a wrapping flex container with gap: 14px,
   * and on a wrapping container that gap is ALSO the row gap - the search
   * field already gets 14px of air above it. The extra 10px was the same
   * distance paid twice, 24px in total, on the surface that has the least
   * height to spare. Removing it buys back a third of what restoring the
   * bar's own padding-block costs, and leaves 14px above the field against
   * 12px below it, which is the balance the rest of the bar uses.
   */
  .search { order: 3; flex-basis: 100%; }
  nav.head-nav {
    order: 4; flex-basis: 100%; margin-left: 0; margin-top: 8px;
    flex-direction: column; flex-wrap: nowrap;
    align-items: stretch; gap: 2px; justify-content: flex-start;
  }
  .js nav.head-nav { display: none; }
  .js nav.head-nav.nav-open { display: flex; max-height: calc(100vh - 160px); overflow-y: auto; overflow-x: hidden; }
  nav.head-nav > a, .menu summary { box-sizing: border-box; display: flex; align-items: center; min-height: 44px; }
  /*
   * The breadcrumb row as its own band (#1651).
   *
   * Below 861px the row follows a text FIELD rather than the end of a bar, and
   * a hairline alone was not enough to say "this is a different thing" - the
   * operator read the two as lying on top of each other. Air does most of the
   * work (see the two rules above); this adds the faintest surface token the
   * sheet has, so the band is legible as a band even at a glance and even
   * where the hairline falls on a dark pixel of the page scrolling underneath.
   *
   * Its own padding drops from .5rem to .25rem here, which pays back another
   * 8px: the row's height is set by the 28px hit target of the house, not by
   * the padding, and that target is the number that must not shrink.
   */
  .crumb-bar { background: var(--wash-faint); }
  .head-crumb { padding: .25rem 0; }

  .lang-menu { margin-left: 0; }
  .menu-panel { position: static; width: auto; grid-template-columns: 1fr; box-shadow: none; backdrop-filter: none; }
  .menu-col + .menu-col { border-left: 0; padding-left: 0; border-top: 1px solid var(--line-2); padding-top: 8px; }
}
@media (min-width: 861px) {
  .sidebar details > summary { display: none; }
}
@media (max-width: 620px) {
  /*
   * padding-INLINE, not the "padding: 0 16px" shorthand this used to be (#1651).
   *
   * That shorthand also wrote padding-block: 0, and because this block comes
   * AFTER the 860px one it silently undid ".bar { padding-block: 12px }" above.
   * Measured on the live site, the defect flipped exactly at the breakpoint:
   * at 621px the bar had 12px/12px and the search field sat 12px above the
   * breadcrumb row; at 620px it had 0/0 and the two touched at 0px. That is
   * the "search lying on the breadcrumb bar" the operator reported - the
   * collision existed as soon as the two rules met, and #1590 only made it
   * VISIBLE by putting a second band directly under the search field.
   *
   * The longhand keeps this rule to the axis it is actually about. Everything
   * else that is a .wrap (the footer, the breadcrumb row) had padding-block 0
   * from ":root"'s ".wrap" anyway, so nothing else moves.
   */
  .wrap { padding-inline: 16px; }
  .prose h2 { margin-top: 2rem; }
  .hero { padding: 1.75rem 1rem; }
}
/* ── Page feedback under the article (#1591) ─────────────────────────────── */
/*
 * Deliberately quiet. It sits under an article the reader has just finished
 * and must not compete with it: a hairline, a heading at body size, two
 * outline buttons. Every colour is a token, so both appearances follow the
 * same ladder the prose does.
 */
.pagefeedback {
  margin: 2.5rem 0 0;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--line-2);
  max-width: 72ch;
}
.pf-heading {
  margin: 0 0 .75rem;
  font-size: 1rem;
  font-weight: 600;
  color: var(--txt);
}
.pf-choice { display: flex; gap: .5rem; flex-wrap: wrap; }
.pf-btn {
  font: inherit;
  cursor: pointer;
  padding: .45rem 1.15rem;
  border-radius: var(--r);
  border: 1px solid var(--line-3);
  background: var(--surface);
  color: var(--txt);
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.pf-btn:hover { background: var(--hover); }
.pf-btn[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-wash); }
.pf-send { border-color: var(--accent); color: var(--accent); }
.pf-send[disabled] { opacity: .55; cursor: default; }
.pf-form { margin-top: 1rem; display: flex; flex-direction: column; gap: .4rem; }
.pf-label { font-size: .9rem; color: var(--txt-sub); }
.pf-label + .pf-input, .pf-label + .pf-textarea { margin-bottom: .5rem; }
.pf-input, .pf-textarea {
  font: inherit;
  width: 100%;
  padding: .5rem .7rem;
  border-radius: var(--r);
  border: 1px solid var(--line-3);
  background: var(--lift);
  color: var(--txt);
}
.pf-textarea { resize: vertical; min-height: 5.5rem; }
.pf-input::placeholder, .pf-textarea::placeholder { color: var(--txt-ghost); }
.pf-suggest { margin: -.25rem 0 .5rem; font-size: .92rem; }
.pf-suggest-heading { margin: 0 0 .3rem; color: var(--txt-sub); }
.pf-suggest ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .2rem; }
.pf-suggest .pf-area { color: var(--txt-cap); font-size: .85em; margin-right: .4rem; }
.pf-privacy { margin: .25rem 0 .75rem; font-size: .85rem; line-height: 1.55; color: var(--txt-cap); }
/*
 * The privacy notice explicitly collects neither name nor email, so a reader
 * who needs an actual reply is otherwise stuck (#1592). Sits inside .pf-form,
 * visible as soon as "Nein" reveals the fields - before anything is sent, not
 * after. Same ladder as the rest of the block: body-size text, one plain link,
 * every colour a token.
 */
.pf-contact { margin: 0 0 .9rem; font-size: .85rem; line-height: 1.55; color: var(--txt-sub); }
.pf-contact-link { color: var(--accent); font-weight: 600; }
.pf-contact-link:hover { text-decoration: underline; }
.pf-contact-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.pf-status { margin: .85rem 0 0; color: var(--txt-body); }
.pf-status.pf-failed { color: var(--txt); }
.pf-send { align-self: flex-start; }
/*
 * The honeypot. NOT display:none and NOT hidden: a robot worth the name skips
 * fields it can see are switched off. It is taken out of the flow, out of the
 * tab order (tabindex="-1" on the input) and out of the accessibility tree
 * (aria-hidden on the wrapper), so no human ever meets it.
 */
.pf-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .01ms !important; scroll-behavior: auto !important; }
}
