/* ==========================================================================
   Ceyu — documentation and blog shell
   Loaded only by /docs and /blog. The marketing pages do not carry it.

   Three columns: grouped navigation on the left, prose in the middle capped at
   a reading measure, "on this page" on the right. The middle column is capped
   rather than filling the space — that cap is what makes a long reference page
   tolerable to read.

   Tokens come from styles.css; this file adds no colours of its own.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */

/* `minmax(0, 1fr)` statt `1fr`, zur Uebereinstimmung mit den beiden
   Breakpoints weiter unten, die das seit jeher so machen.

   Ein `1fr`-Track hat als Minimum `auto`, also die Mindestbreite seines
   Inhalts, und kann dadurch ueber seinen Container hinauswachsen. Beim
   Ueberlauf, der diese Zeile veranlasst hat, war das nachweislich NICHT die
   Ursache — der Track sass korrekt, die Tabelle darin nicht (siehe
   .prose-table in styles.css). Die Zeile bleibt trotzdem, weil die
   einspaltige Grundregel genau dort greift, wo der Platz am knappsten ist,
   und weil zwei von drei Regeln in dieser Datei es ohnehin schon so halten. */
.doc-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

@media (min-width: 1000px) {
  .doc-shell {
    grid-template-columns: 268px minmax(0, 1fr);
  }
}

@media (min-width: 1280px) {
  .doc-shell {
    grid-template-columns: 268px minmax(0, 1fr) 244px;
  }
}

/* --------------------------------------------------------------------------
   Left rail
   -------------------------------------------------------------------------- */

.doc-nav {
  position: sticky;
  top: var(--header-h);
  max-height: calc(100vh - var(--header-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-8) var(--s-5) var(--s-12) 0;
  border-inline-end: 1px solid var(--line);
  scrollbar-width: thin;
}

@media (max-width: 999px) {
  /* On narrow screens the rail becomes a disclosure above the article instead
     of a sticky column — a 268px sidebar on a phone is just a wall. */
  .doc-nav {
    position: static;
    max-height: none;
    overflow: visible;
    border-inline-end: 0;
    border-bottom: 1px solid var(--line);
    padding: var(--s-4) 0;
  }

  .doc-nav > .doc-nav__inner {
    display: none;
  }

  .doc-nav[data-open="true"] > .doc-nav__inner {
    display: block;
    padding-top: var(--s-4);
  }
}

.doc-nav__toggle {
  display: none;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-3) var(--s-4);
  border: var(--stroke);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
  font-size: var(--t-sm);
  font-weight: 700;
}

@media (max-width: 999px) {
  .doc-nav__toggle {
    display: flex;
  }
}

.doc-nav__toggle .chev {
  width: 15px;
  height: 15px;
  margin-inline-start: auto;
  color: var(--ink-50);
  transition: rotate var(--base) var(--ease);
}

.doc-nav[data-open="true"] .doc-nav__toggle .chev {
  rotate: 90deg;
}

.doc-nav__group + .doc-nav__group {
  margin-top: var(--s-6);
}

.doc-nav__title {
  padding: var(--s-2) var(--s-4);
  margin-bottom: var(--s-1);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.doc-nav a {
  display: block;
  padding: 0.4375rem var(--s-4);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  color: var(--ink-60);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}

.doc-nav a:hover {
  background: var(--ink-06);
  color: var(--ink);
}

/* The current page reads as a filled pill. In a list this dense an underline
   or a left bar simply disappears. */
.doc-nav a[aria-current="page"],
.doc-nav a[aria-current="true"] {
  background: var(--accent-10);
  color: var(--accent-strong);
  font-weight: 600;
}

.doc-nav details > summary {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 0.4375rem var(--s-4);
  border-radius: var(--r-md);
  cursor: pointer;
  list-style: none;
  font-size: var(--t-sm);
  color: var(--ink-60);
}

.doc-nav details > summary::-webkit-details-marker {
  display: none;
}

.doc-nav details > summary:hover {
  background: var(--ink-06);
  color: var(--ink);
}

.doc-nav details > summary .chev {
  width: 14px;
  height: 14px;
  flex: none;
  margin-inline-start: auto;
  color: var(--ink-40);
  transition: rotate var(--base) var(--ease);
}

.doc-nav details[open] > summary .chev {
  rotate: 90deg;
}

.doc-nav__sub {
  padding-inline-start: var(--s-4);
  margin-block: var(--s-1);
  border-inline-start: 1px solid var(--line);
}

/* --------------------------------------------------------------------------
   Middle column
   -------------------------------------------------------------------------- */

.doc-main {
  min-width: 0;
  padding: var(--s-8) 0 var(--s-16);
}

@media (min-width: 1000px) {
  .doc-main {
    padding-inline: clamp(1.5rem, 3vw, 3rem);
  }
}

.doc-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
  font-size: var(--t-sm);
}

.doc-breadcrumb a {
  color: var(--accent-strong);
}

.doc-breadcrumb a:hover {
  color: var(--accent-strong-hover);
}

.doc-breadcrumb .sep {
  color: var(--ink-40);
}

.doc-breadcrumb [aria-current="page"] {
  color: var(--ink-60);
}

.doc-head {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-5);
  margin-bottom: var(--s-6);
}

.doc-head h1 {
  font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.08;
}

/* Copy page — hands over the raw Markdown for pasting into an editor or a
   model. One button: the dropdown beside it offered three ways to do almost
   the same thing, which is three decisions in place of the one anybody came
   for. */
.copy-page {
  flex: none;
  display: inline-flex;
  border: var(--stroke);
  border-radius: var(--r-round);
  overflow: hidden;
  background: var(--ground);
  box-shadow: var(--shadow-1);
  transition:
    box-shadow 120ms var(--ease),
    translate 120ms var(--ease),
    background 160ms var(--ease);
}

/* Lifts by exactly its shadow offset and drops into it on the press, the same
   mechanical click every other control on the site uses instead of an
   elevation animation. */
.copy-page:hover {
  box-shadow: var(--shadow-2);
  translate: -2px -2px;
}

.copy-page:active {
  box-shadow: none;
  translate: var(--press) var(--press);
}

.copy-page button {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-70);
  white-space: nowrap;
  transition: background 120ms var(--ease), color 120ms var(--ease);
}

.copy-page button:hover {
  background: var(--ink-06);
  color: var(--ink);
}

.copy-page svg {
  width: 15px;
  height: 15px;
  flex: none;
}

/* The two icons occupy one square and cross over inside it, so nothing on
   either side of them moves while the state changes. */
.copy-page__icons {
  position: relative;
  display: grid;
  place-items: center;
  width: 15px;
  height: 15px;
  flex: none;
}

.copy-page__icon {
  grid-area: 1 / 1;
  /* Fast on purpose: this is an acknowledgement, not an event. Anything
     slower than about a tenth of a second reads as the page thinking about
     it, and someone pressing twice in a row would see the second press land
     in the middle of the first animation. */
  transition: opacity 110ms var(--ease), scale 110ms var(--ease),
    rotate 110ms var(--ease);
}

.copy-page__icon--done {
  opacity: 0;
  scale: 0.4;
  rotate: -25deg;
  color: var(--ink);
}

.copy-page.is-copied .copy-page__icon--rest,
.copy-page.is-failed .copy-page__icon--rest {
  opacity: 0;
  scale: 0.4;
  rotate: 25deg;
}

.copy-page.is-copied .copy-page__icon--done {
  opacity: 1;
  scale: 1;
  rotate: 0deg;
}

/* Confirmation is a fill, not a colour swap on the text: the label has to
   stay at full contrast while it says something the reader needs. */
.copy-page.is-copied {
  background: var(--pop-mint);
}

.copy-page.is-failed {
  background: var(--pop-coral);
}

.copy-page.is-copied button,
.copy-page.is-failed button {
  color: var(--ink);
}

/* One short nudge, the same hard offset the rest of the site uses to say a
   control was pressed. It restarts on every press — see the reflow in
   app.js — so the twentieth press acknowledges as clearly as the first. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes copy-page-confirm {
    0% {
      translate: var(--press) var(--press);
    }
    55% {
      translate: -2px -2px;
    }
    100% {
      translate: 0 0;
    }
  }

  .copy-page.is-copied,
  .copy-page.is-failed {
    animation: copy-page-confirm 260ms var(--ease-out) both;
  }
}

/* Where there is no clipboard there is nothing to press. */
[data-copy-unavailable] {
  opacity: 0.55;
  pointer-events: none;
}

/* Heading anchors: revealed on hover, always reachable by keyboard. */
.prose :is(h2, h3, h4) > .anchor {
  opacity: 0;
  margin-inline-start: var(--s-2);
  font-weight: 400;
  color: var(--ink-40);
  transition: opacity var(--fast) var(--ease), color var(--fast) var(--ease);
}

.prose :is(h2, h3, h4):hover > .anchor,
.prose .anchor:focus-visible {
  opacity: 1;
}

.prose .anchor:hover {
  color: var(--accent-strong);
}

/* --------------------------------------------------------------------------
   Code blocks with a title bar
   -------------------------------------------------------------------------- */

.code-block {
  border: var(--stroke);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface-2);
  box-shadow: var(--shadow-1);
}

.code-block__bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3) var(--s-2) var(--s-4);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.code-block__title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-70);
}

.code-block__lang {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
}

.code-block__actions {
  display: flex;
  gap: var(--s-1);
  margin-inline-start: auto;
}

.code-block__actions button {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-sm);
  color: var(--ink-50);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}

.code-block__actions button:hover {
  background: var(--ink-10);
  color: var(--ink);
}

.code-block__actions svg {
  width: 15px;
  height: 15px;
}

.code-block pre {
  margin: 0;
  padding: var(--s-4) var(--s-5);
  border: 0;
  border-radius: 0;
  background: transparent;
  font-size: 0.875rem;
  line-height: 1.65;
}

/* The generic copy button that app.js bolts onto bare <pre> blocks is not
   wanted here — this block has its own in the title bar. */
.code-block pre .copy-btn {
  display: none;
}

/* Minimal, legible token colours. Deliberately few: six hues in a code block
   is decoration, not information. */
.tok-com {
  color: var(--ink-50);
  font-style: italic;
}
.tok-str {
  color: #0a7d4b;
}
.tok-key {
  color: #9333ea;
}
.tok-num {
  color: #b45309;
}
.tok-fn {
  color: var(--accent-strong);
}

/* --------------------------------------------------------------------------
   Right rail
   -------------------------------------------------------------------------- */

.doc-toc {
  display: none;
  position: sticky;
  top: var(--header-h);
  max-height: calc(100vh - var(--header-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-8) 0 var(--s-12);
  font-size: var(--t-sm);
}

@media (min-width: 1280px) {
  .doc-toc {
    display: block;
  }
}

.doc-toc__title {
  margin-bottom: var(--s-3);
  padding-inline-start: var(--s-4);
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--ink);
}

.doc-toc a {
  display: block;
  padding: 0.3125rem 0 0.3125rem var(--s-4);
  border-inline-start: 2px solid var(--line);
  color: var(--ink-60);
  transition: color var(--fast) var(--ease),
    border-color var(--fast) var(--ease);
}

.doc-toc a:hover {
  color: var(--ink);
}

.doc-toc a[aria-current="true"] {
  font-weight: 500;
  color: var(--accent-strong);
  border-inline-start-color: var(--accent-strong);
}

.doc-toc .lvl-3 {
  padding-inline-start: var(--s-8);
}

/* --------------------------------------------------------------------------
   Previous / next
   -------------------------------------------------------------------------- */

.doc-pager {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  margin-top: var(--s-16);
  padding-top: var(--s-8);
  border-top: 1px solid var(--line);
}

.doc-pager a {
  display: grid;
  gap: var(--s-1);
  padding: var(--s-4) var(--s-5);
  border: var(--stroke);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--fast) var(--ease),
    translate var(--fast) var(--ease), background var(--fast) var(--ease);
}

.doc-pager a:hover {
  box-shadow: var(--shadow-2);
  translate: -2px -2px;
  background: var(--surface-2);
}

.doc-pager .dir {
  font-size: var(--t-xs);
  color: var(--ink-50);
}

.doc-pager .name {
  font-weight: 600;
  color: var(--accent-strong);
}

.doc-pager a.is-next {
  text-align: end;
}

/* --------------------------------------------------------------------------
   Header search
   -------------------------------------------------------------------------- */

.doc-search {
  position: relative;
  flex: 1 1 auto;
  max-width: 540px;
  margin-inline: var(--s-6);
}

/* The documentation bar carries everything the site bar does plus a search
   field, so it runs out of room roughly 250px earlier. Below that the links
   step aside — they are all in the burger — and the field keeps the space,
   because search is what someone came to a documentation page for.

   Without this the account button was pushed past the right edge at 1024 and
   quietly cut off by the body's overflow clip: no scrollbar to notice, just a
   missing control. */
@media (max-width: 1230px) {
  .header--docs .nav {
    display: none;
  }

  .header--docs .burger {
    display: flex;
  }
}

@media (max-width: 700px) {
  /* Narrower still and the field itself is what has to give: a wordmark, a
     search box, a language button and an account button do not fit. The
     documentation page keeps its own in-page search field. */
  .header--docs .doc-search {
    display: none;
  }
}

.doc-search__field {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border: var(--stroke);
  border-radius: var(--r-round);
  background: var(--surface-2);
  color: var(--ink-50);
  font-size: var(--t-sm);
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--fast) var(--ease),
    translate var(--fast) var(--ease);
}

.doc-search__field:hover,
.doc-search__field:focus-within {
  box-shadow: var(--shadow-2);
  translate: -2px -2px;
}

.doc-search__field > svg {
  width: 16px;
  height: 16px;
  flex: none;
}

.doc-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: none;
  background: none;
  color: var(--ink);
}

.doc-search input::placeholder {
  color: var(--ink-50);
}

.doc-search kbd {
  flex: none;
  padding: 0.05em 0.4em;
  font-size: 0.6875rem;
}

@media (max-width: 980px) {
  .doc-search {
    display: none;
  }
}

.doc-results {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + var(--s-2));
  z-index: 120;
  display: none;
  max-height: 60vh;
  overflow-y: auto;
  padding: var(--s-2);
  background: var(--ground);
  border: var(--stroke);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
}

.doc-results[data-open="true"] {
  display: block;
}

.doc-results a {
  display: grid;
  gap: 2px;
  padding: var(--s-3);
  border-radius: var(--r-md);
}

.doc-results a:hover,
.doc-results a[data-active="true"] {
  background: var(--ink-06);
}

.doc-results .r-title {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink);
}

.doc-results .r-crumb {
  font-size: var(--t-xs);
  color: var(--ink-50);
}

.doc-results .r-empty {
  padding: var(--s-4);
  font-size: var(--t-sm);
  color: var(--ink-60);
}

/* --------------------------------------------------------------------------
   Blog overview: search and category chips
   -------------------------------------------------------------------------- */

/* The filter row is one drawn object per control, in the same language as
   the rest of the site: ink outline, hard offset, mechanical press. */
.blog-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4);
  margin-top: var(--s-10);
}

.blog-tools input[type="search"] {
  flex: 1 1 260px;
  min-width: 0;
  padding: 12px var(--s-4);
  border: var(--stroke);
  border-radius: var(--r-md);
  background: var(--surface-2);
  box-shadow: var(--shadow-1);
  font-size: var(--t-sm);
  transition: box-shadow var(--fast) var(--ease),
    translate var(--fast) var(--ease);
}

.blog-tools input[type="search"]:focus-visible {
  outline: none;
  box-shadow: var(--shadow-2);
  translate: -2px -2px;
}

.blog-tools input[type="search"]::placeholder {
  color: var(--ink-60);
}

.blog-cats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.blog-cats button {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 10px var(--s-4);
  border: var(--stroke);
  border-radius: var(--r-round);
  background: var(--surface-2);
  box-shadow: var(--shadow-1);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--ink);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
    box-shadow var(--fast) var(--ease), translate var(--fast) var(--ease);
}

.blog-cats button:hover {
  background: var(--tile-purple);
  box-shadow: var(--shadow-2);
  translate: -2px -2px;
}

.blog-cats button:active {
  box-shadow: none;
  translate: var(--press) var(--press);
}

.blog-cats button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--ground);
}

/* The count is a chip inside a chip, so it needs its own fill to stay
   readable when the button flips to ink. */
.blog-cats .cnt {
  padding: 1px 7px;
  border-radius: var(--r-round);
  background: var(--ink-10);
  font-size: 11px;
  font-weight: 800;
  color: var(--ink-70);
}

.blog-cats button[aria-pressed="true"] .cnt {
  background: rgba(255, 255, 255, 0.22);
  color: var(--ground);
}

@media (prefers-reduced-motion: reduce) {
  .blog-cats button:hover,
  .blog-cats button:active,
  .blog-tools input[type="search"]:focus-visible {
    translate: 0 0;
  }
}

.post-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.post-card {
  position: relative;
}

/* ==========================================================================
   Leseanzeiger, Buchfigur, Tabellen und Schrift, die ankommt
   ========================================================================== */

/* Der Abschnitt, in dem man gerade liest. Die Marke ist die Kante links —
   dieselbe Linie, die das Verzeichnis ohnehin führt, nur ausgefüllt und
   dicker. Ein zweites Element (Punkt, Pfeil, Hintergrund) wäre eine zweite
   Aussage für dieselbe Sache. */
.doc-toc a.is-current {
  font-weight: 700;
  color: var(--ink);
  border-inline-start-color: var(--accent-strong);
  border-inline-start-width: 3px;
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--accent-strong) 10%, transparent),
    transparent 62%
  );
}

.doc-toc a {
  border-radius: 0 6px 6px 0;
  scroll-margin: 12px;
}

.doc-toc a:focus-visible {
  outline: 2px solid var(--accent-strong);
  outline-offset: -2px;
}

/* ── Die Buchfigur ─────────────────────────────────────────────────────────
   Sie steht oben in der rechten Spalte — dort, wo der Blick beim Ankommen
   hinfällt, und dort, wo eine Sprechblase Platz hat, ohne über den Text zu
   fallen. Darunter steht, worauf man gerade steht, dann das Verzeichnis.

   Eine Figur, nicht mehr: mehrere wären Dekoration in einem Text, den jemand
   liest, um etwas zu erledigen. */

.doc-mascot {
  position: relative;
  margin-bottom: var(--s-5);
  padding-inline-start: 2px;
  min-height: 92px;
}

.doc-mascot__btn {
  display: inline-flex;
  padding: 4px;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: 14px;
  transition: translate 160ms var(--ease), rotate 160ms var(--ease);
}

.doc-mascot__btn:hover {
  translate: 0 -3px;
  rotate: -3deg;
}

.doc-mascot__btn:active {
  translate: 0 1px;
  rotate: 0deg;
}

.doc-mascot__btn:focus-visible {
  outline: 2px solid var(--accent-strong);
  outline-offset: 2px;
}

/* Die Blase steht UNTER der Figur, weil die Figur jetzt oben sitzt: über ihr
   wäre der Seitenkopf. Sie nimmt keinen Platz weg, solange nichts gesagt
   wird — deshalb absolut. */
.doc-mascot__say {
  position: absolute;
  top: calc(100% - 10px);
  inset-inline-start: 0;
  z-index: 2;
  width: min(250px, 100%);
  margin: 0;
  padding: 11px 13px;
  border: 2px solid var(--ink);
  border-radius: 14px;
  background: var(--tile-sky, #ddf1ff);
  box-shadow: 3px 3px 0 var(--ink);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink);
  pointer-events: none;
  opacity: 0;
  scale: 0.9;
  transform-origin: 34px -6px;
  transition:
    opacity 160ms var(--ease),
    scale 160ms var(--ease);
}

/* Die Blase kommt aus dem Zipfel heraus: sie wächst aus dem Punkt, an dem
   sie das Buch berührt, statt als Rechteck einzublenden. Ein kurzer
   Überschwung macht daraus eine Blase und keine Box. */
.doc-mascot__say.is-on {
  opacity: 1;
  scale: 1;
  animation: doc-say-pop 320ms var(--ease-out);
}

@keyframes doc-say-pop {
  0% {
    scale: 0.86;
  }
  62% {
    scale: 1.035;
  }
  100% {
    scale: 1;
  }
}

/* Der Zipfel, gezeichnet wie alles andere: Fläche plus Kontur. Er zeigt nach
   oben, zur Figur. */
.doc-mascot__say::after {
  content: "";
  position: absolute;
  bottom: 100%;
  inset-inline-start: 26px;
  width: 14px;
  height: 14px;
  background: var(--tile-sky, #ddf1ff);
  border-inline-start: 2px solid var(--ink);
  border-block-start: 2px solid var(--ink);
  rotate: 45deg;
  translate: 0 8px;
}

/* Buchstabenweiser Aufbau. Der ganze Satz steht im Dokument, die Zeichen
   werden nur nacheinander sichtbar geschaltet — ein Vorleser bekommt den
   fertigen Satz und kein Stottern. */
.doc-say__ch {
  display: inline-block;
  opacity: 0;
  translate: 0 4px;
  transition: opacity 130ms var(--ease), translate 130ms var(--ease);
}

.doc-say__ch.is-space {
  white-space: pre;
}

.doc-say__ch.is-on {
  opacity: 1;
  translate: 0 0;
}

.doc-mascot__lines {
  display: none;
}

/* ── Wo man gerade steht ───────────────────────────────────────────────────
   Die Liste sagt, was auf der Seite steht; diese Zeile sagt, wo man darin
   ist. Versteckt, bis der Leseanzeiger etwas Sicheres hat. */

.doc-here {
  margin: 0 0 var(--s-4);
  padding: 9px 12px;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--surface, #f7f5fb);
  box-shadow: 2px 2px 0 var(--ink);
}

.doc-here__label {
  display: block;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-50, #15161799);
}

.doc-here__name {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .doc-mascot__btn,
  .doc-mascot__say,
  .doc-say__ch {
    transition: none;
  }

  .doc-mascot__say.is-on {
    animation: none;
  }

  .doc-say__ch {
    opacity: 1;
    translate: 0 0;
  }
}

/* ── Hinweiskästen mit Kontur ──────────────────────────────────────────────
   Der Kasten hatte eine Haarlinie und lag flach auf dem Papier. Auf einer
   Seite, deren Knöpfe und Karten alle einen harten Versatz tragen, liest sich
   das wie ein Fremdkörper — jetzt steht er auf demselben Schatten. */

.prose .callout {
  border: 2px solid var(--ink);
  border-radius: 14px;
  box-shadow: 4px 4px 0 var(--ink);
  padding: var(--s-5) var(--s-6);
}

.prose .callout__label {
  color: var(--ink);
  opacity: 0.72;
}

/* ── Tabellen ──────────────────────────────────────────────────────────────
   Eine Tabelle ist die kürzeste Form für „was gilt wofür". Sie bekommt
   dieselbe Kontur wie die Karten, einen Kopf, der beim Scrollen stehen
   bleibt, und auf schmalen Schirmen einen eigenen Scrollbereich — eine
   Tabelle, die die Seite breiter macht, zerstört das Lesen daneben. */

/* `display: grid` ist hier kein Layout, sondern die Reparatur: als
   gewoehnlicher Block-Container blieb die Tabelle bei `width: 100%` rund vier
   Pixel schmaler als der Rahmen, weil ein Scroll-Container die verfuegbare
   Breite anders aufloest. Sichtbar war das als Streifen rechts, in dem die
   Zeilenlinien vor dem Rand aufhoerten und der Rahmen doppelt wirkte. Als
   Rasterelement dehnt sich die Tabelle auf die volle Spaltenbreite. */
/* Der Rahmen sitzt auf dem Kasten, nicht auf der Tabelle: eine Tabelle mit
   `border-collapse: collapse` ignoriert `border-radius`, also gäbe es sonst
   keine runden Ecken. */
.doc-table-wrap {
  --doc-table-frame: 2px;
  margin: var(--s-6) 0;
  overflow-x: auto;
  border: var(--doc-table-frame) solid var(--ink);
  border-radius: 14px;
  box-shadow: 4px 4px 0 var(--ink);
  background: var(--surface, #fff);
}

.doc-table {
  /* Gemessen: in diesem Scroll-Kasten löst `width: 100%` auf 730 statt 734
     Pixel auf — die Rahmenbreite wird zweimal abgezogen. Sichtbar war das als
     Streifen rechts, in dem die Zeilenlinien vor dem Rand aufhörten und der
     Rahmen doppelt wirkte. Der Zuschlag hängt an derselben Eigenschaft wie
     der Rahmen, damit beide nicht auseinanderlaufen können. */
  width: calc(100% + var(--doc-table-frame) * 2);
  /* Der Zuschlag darf keinen Scrollbalken erzeugen: der negative Aussenabstand
     nimmt ihn aus der Ueberlaufrechnung heraus, gemalt wird trotzdem die volle
     Breite. Ohne ihn stand unter jeder Tabelle ein Balken fuer vier Pixel. */
  margin-inline-end: calc(var(--doc-table-frame) * -2);
  border-collapse: collapse;
  font-size: var(--t-sm);
}

.doc-table th,
.doc-table td {
  padding: 12px 16px;
  text-align: start;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}

.doc-table thead th {
  position: sticky;
  top: 0;
  background: var(--tile-purple, #efe6ff);
  border-bottom: 2px solid var(--ink);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

.doc-table tbody tr:last-child th,
.doc-table tbody tr:last-child td {
  border-bottom: 0;
}

.doc-table tbody tr:hover {
  background: color-mix(in srgb, var(--accent-strong) 5%, transparent);
}

.doc-table td code,
.doc-table th code {
  white-space: nowrap;
}

/* Ja/Nein-Zellen tragen ein Zeichen, damit die Spalte beim Überfliegen
   antwortet, ohne dass man Wörter liest. */
.doc-table [data-yes]::before {
  content: "●";
  margin-inline-end: 8px;
  color: var(--ok, #176b3a);
}

.doc-table [data-no]::before {
  content: "○";
  margin-inline-end: 8px;
  color: var(--ink-40);
}

/* ── Schrift, die ankommt ──────────────────────────────────────────────────
   Buchstabe für Buchstabe, aber kurz: die ganze Zeile steht nach einer
   knappen halben Sekunde. Der Text liegt vollständig im Dokument — die
   Animation verzögert nur seine Sichtbarkeit. */

.typeset__ch {
  display: inline-block;
  opacity: 0;
  animation: typeset-in 260ms var(--ease, cubic-bezier(0.16, 1, 0.3, 1)) both;
}

@keyframes typeset-in {
  from {
    opacity: 0;
    translate: 0 0.32em;
    filter: blur(2px);
  }
  to {
    opacity: 1;
    translate: 0 0;
    filter: blur(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .typeset__ch {
    animation: none;
    opacity: 1;
  }
}

/* Für Vorleser gedacht, nicht fürs Auge: die Tabellenüberschrift wiederholt,
   was zwei Zeilen darüber als Überschrift steht — sichtbar wäre sie doppelt
   gemoppelt, weggelassen fehlte sie dem, der die Seite hört. */
.doc-table-wrap .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
