/* ──────────────────────────────────────────────────────────────────────────
   Keln site nav on docs.keln.ai.

   Ported from keln-website/src/components/SiteNav.astro. Values are copied
   from there deliberately (64px bar, #fafafa canvas, 21px/-0.045em wordmark)
   so the two headers line up pixel-for-pixel when a reader crosses between
   keln.ai and docs.keln.ai.

   Loaded after keln.css, which owns the brand tokens.
   ────────────────────────────────────────────────────────────────────────── */

:root {
  --kn-bg: #fafafa;
  --kn-ink: #0a0a0a;
  --kn-mut: #5a5a5a;
  --kn-line: #efefec;
  --kn-line-strong: #dededa;
  --kn-bar-h: 64px;
  /* Nav-link tokens, from keln-website/src/styles/tokens/colors.css. Links use
     --ink2, not --muted; only "Log in" is --muted, per SiteNav.astro. */
  --kn-ink2: #3a3a3a;
  --kn-panel2: #f5f5f3;
  --kn-nav-active-bg: #f0f0ee;
  --kn-nav-active-fg: #0a0a0a;
  /* Filled buttons. Button.astro uses --ink (#0d0d0d), which is also the dark
     page background — so these must invert per scheme or the button vanishes. */
  --kn-btn-bg: #0d0d0d;
  --kn-btn-fg: #ffffff;
  --kn-btn-bg-hover: #2a2a2a;
}
[data-md-color-scheme="slate"] {
  --kn-bg: #0d0d0d;
  --kn-ink: #ececf1;
  --kn-mut: #a2a2ab;
  --kn-line: #26261f;
  --kn-line-strong: #34342f;
  --kn-ink2: #d4d4d8;
  --kn-panel2: #1e1e21;
  --kn-nav-active-bg: #1e1e21;
  --kn-nav-active-fg: #ececf1;
  --kn-btn-bg: #ececf1;
  --kn-btn-fg: #0d0d0d;
  --kn-btn-bg-hover: #ffffff;
}

/* ── Content width ──────────────────────────────────────────────────────── */

/* .md-grid drives the header, the tabs and the main column, so setting it once
   keeps all three aligned. Values from .kwrap in keln-website global.css
   (1440px / 48px); Material's default 61rem = 1220px is 220px narrower.
   The 32px step at tablet mirrors .sec's override in the same file. */
.md-grid {
  max-width: 1440px;
  padding: 0 48px;
}

/* Material leaves 50px between the tab row and the first heading: a 30px top
   margin on .md-main__inner, 12px of content padding, and an 8px spacer.
   The spacer stays — it is the scroll offset for heading anchors — and the
   other two come down to 16px total. */
.md-main__inner {
  margin-top: 16px;
}
.md-content__inner {
  padding-top: 0;
  /* Adds to Material's own 24px bottom margin and the switcher's 24px top
     padding for ~100px of air before it. 24px alone ran the last line of the
     page straight into the switcher. */
  padding-bottom: 32px;
}

/* ── Header bar ─────────────────────────────────────────────────────────── */

.keln-header {
  /* keln.ai's body sets 1.6 and the header inherits it. Without this the
     wordmark, Log in and the buttons all come out short of the real ones. */
  line-height: 1.6;
  background: var(--kn-bg);
  color: var(--kn-ink);
  border-bottom: 1px solid var(--kn-line);
  box-shadow: none;
}

.knav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--kn-bar-h);
}

.knav-left {
  display: flex;
  align-items: center;
  gap: 32px;
  min-width: 0;
}

.knav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--kn-ink);
  flex: none;
}
.knav-brand img {
  display: block;
  width: 30px;
  height: 30px;
}
.knav-word {
  /* Wordmark uses the separate Semibold family, as SiteNav.astro does — Geist
     has no 600-normal cut in the regular family. */
  font-family: 'Geist Semibold', var(--md-text-font, inherit);
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.045em;
}

.knav-links {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}
/* Matches .kln-navlink in keln-website/src/components/ui/NavLink.astro: a pill,
   not flat text. Hover moves the background, not the colour; the active item
   carries a filled pill. */
.knav-link {
  font: 400 14px var(--md-text-font, inherit);   /* shorthand resets line-height to normal, as on the site */
  letter-spacing: -0.01em;
  color: var(--kn-ink2);
  padding: 7px 13px;
  border-radius: 999px;
  white-space: nowrap;
  transition: background 0.15s ease;
}
.knav-link:hover {
  background: var(--kn-panel2);
  color: var(--kn-ink2);
}
.knav-link--current {
  background: var(--kn-nav-active-bg);
  color: var(--kn-nav-active-fg);
  font-weight: 500;
}

.knav-cta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
.knav-login {
  font-size: 14px;
  color: var(--kn-mut);
  padding: 7px 12px;
  border-radius: 8px;
  white-space: nowrap;
}
.knav-signup {
  font-size: 14px;
  line-height: normal;
  font-weight: 500;
  color: var(--kn-btn-fg);
  background: var(--kn-btn-bg);
  border: 1.5px solid var(--kn-btn-bg);
  border-radius: 999px;
  padding: 8px 18px;
  white-space: nowrap;
  transition: opacity 0.15s;
}
.knav-signup:hover {
  opacity: 0.85;
}

/* Palette (light/dark) toggle — Material renders a stacked pair of labels and
   shows whichever matches the active scheme. */
.keln-header .md-header__button.md-icon {
  color: var(--kn-mut);
  margin: 0;
  padding: 6px;
  width: 32px;
  height: 32px;
  transition: color 0.15s;
}
.keln-header .md-header__button.md-icon:hover {
  opacity: 1;   /* colour comes from the shared hover rule below */
}

/* Header search icon — drawer widths only, see the responsive block below. */
.knav-search-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  cursor: pointer;
  color: var(--kn-mut);
}
.knav-search-btn svg {
  width: 22px;
  height: 22px;
  fill: currentcolor;
}

/* Mobile drawer trigger — opens the docs nav, not a second marketing menu. */
.knav-burger {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-left: -8px;
  cursor: pointer;
  color: var(--kn-ink);
}
.knav-burger svg {
  width: 24px;
  height: 24px;
  fill: currentcolor;
}

/* ── Tabs (Docs / API reference) ────────────────────────────────────────── */

.md-tabs {
  background: var(--kn-bg);
  color: var(--kn-ink);
  border-bottom: 1px solid var(--kn-line);
}
.md-tabs__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--kn-mut);
  opacity: 1;
  font-size: 14px;
  margin-top: 0;
  padding: 14px 0;
}

/* Hand-drawn stroke icons (overrides/.icons/keln/). Material's sprite CSS sets
   fill on tab SVGs, which would blot out an outline icon — hence the resets. */
.md-tabs__icon {
  display: inline-flex;
  flex: none;
}
.md-tabs__icon svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentcolor;
  stroke-width: 1.5;
  vertical-align: middle;
}
/* Material puts the active marker on the <li>, not the <a>. */
.md-tabs__item--active .md-tabs__link {
  color: var(--kn-ink);
  font-weight: 500;
  box-shadow: inset 0 -2px 0 var(--kn-ink);
}

/* ── Search ─────────────────────────────────────────────────────────────── */

/* The visible trigger, at the top of the left sidebar (overrides/main.html). */
.knav-search {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin: 0 0 20px;   /* top edge lines up with the H1 and the ToC */
  padding: 9px 12px;
  border: 1px solid var(--kn-line-strong);
  /* 8px — keln.ai has no radius scale, but its utility controls (.cw-copy,
     .norm-in) are 7-8px, while 999px is reserved for nav and CTA pills. */
  border-radius: 8px;
  background: var(--md-default-bg-color);
  color: var(--kn-mut);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.knav-search:hover {
  border-color: var(--kn-mut);
  color: var(--kn-ink);
}
.knav-search__icon {
  display: flex;
  flex: none;
}
.knav-search__icon svg {
  width: 18px;
  height: 18px;
  fill: currentcolor;
}
.knav-search__label {
  flex: 1;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.knav-search__key {
  flex: none;
  font-family: inherit;
  font-size: 11px;
  line-height: 1;
  color: var(--kn-mut);
  background: var(--md-default-bg-color);
  border: 1px solid var(--kn-line-strong);
  border-radius: 6px;
  padding: 4px 6px;
}
.knav-search__key span {
  font-size: 12px;
}

/* ── Search results panel ─────────────────────────────────────────────────
   Written against mkdocs-material 9.7.7 class names. Restyles only what is
   inside the results panel; the input field stays stock (see the note in the
   responsive block below about why). */

/* Panel: hairline border and a soft shadow, like the code-window cards,
   instead of Material's 40% black drop shadow. */
/* Material sets the open-state shadow with this exact selector shape, so the
   override has to match it (see the note at the top of keln.css). */
[data-md-toggle="search"]:checked ~ .md-header .md-search__output {
  box-shadow: 0 0 0 1px var(--kn-line-strong), 0 12px 32px rgba(0, 0, 0, 0.10);
}
[data-md-color-scheme="slate"] [data-md-toggle="search"]:checked ~ .md-header .md-search__output {
  box-shadow: 0 0 0 1px var(--kn-line-strong), 0 12px 32px rgba(0, 0, 0, 0.55);
}

/* Reaching the end of the results must not scroll the page behind the dialog.
   Material leaves this at the browser default. The scrollbar thumb also turned
   accent blue on hover; keep it muted. */
.md-search__scrollwrap {
  overscroll-behavior: contain;
}
.md-search__scrollwrap:hover {
  scrollbar-color: var(--kn-mut) transparent;
}
.md-search__scrollwrap::-webkit-scrollbar-thumb:hover {
  background-color: var(--kn-mut);
}

/* "N matching documents": muted caption on the page surface with a hairline
   under it, not a grey band. */
.md-search-result__meta {
  background: var(--md-default-bg-color);
  color: var(--kn-mut);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.01em;
  box-shadow: inset 0 -1px 0 var(--kn-line);
}

/* Selected / hovered result: the same neutral fill the active sidebar item
   uses, in place of the blue accent tint. */
.md-search-result__link:focus,
.md-search-result__link:hover {
  background-color: var(--kn-nav-active-bg);
}

/* Section headings inside a hit: Material uses 700, but the Geist family here
   has real cuts only at 400 and 500, so anything heavier is synthesised.
   500 is the Medium file, the weight page headings use. Material's rule is
   .md-search-result .md-typeset h2, so the selector has to match it. */
.md-search-result .md-typeset h2 {
  font-weight: 500;
}
/* Page titles were 400, one real step up so a page hit stands apart from the
   section hits under it. */
.md-search-result .md-typeset h1 {
  font-weight: 500;
}
/* Page hits carry the same book glyph as the Docs tab, inline before the
   title. The SVG is overrides/.icons/keln/book.svg, drawn as a mask so it
   takes the muted ink in both schemes. Section hits stay plain. */
.md-search-result .md-typeset h1::before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 16px;
  margin-right: 8px;
  vertical-align: -2px;
  background-color: var(--kn-mut);
  -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 6.6C10.6 5.3 8.7 4.6 6.5 4.6H4.2a.6.6 0 0 0-.6.6v11.9a.6.6 0 0 0 .6.6h2.3c2.2 0 4.1.7 5.5 2'/%3E%3Cpath d='M12 6.6c1.4-1.3 3.3-2 5.5-2h2.3a.6.6 0 0 1 .6.6v11.9a.6.6 0 0 1-.6.6h-2.3c-2.2 0-4.1.7-5.5 2'/%3E%3Cpath d='M12 6.6v13.1'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 6.6C10.6 5.3 8.7 4.6 6.5 4.6H4.2a.6.6 0 0 0-.6.6v11.9a.6.6 0 0 0 .6.6h2.3c2.2 0 4.1.7 5.5 2'/%3E%3Cpath d='M12 6.6c1.4-1.3 3.3-2 5.5-2h2.3a.6.6 0 0 1 .6.6v11.9a.6.6 0 0 1-.6.6h-2.3c-2.2 0-4.1.7-5.5 2'/%3E%3Cpath d='M12 6.6v13.1'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Excerpts are capped at about two lines. Material prints the whole matching
   section under the heading (every paragraph, list and table); the cap works
   on the box, so table text that arrives as bare text nodes is capped too.
   The fade sits in the bottom padding, so a short excerpt that never reaches
   it is not faded. Heights: section heading + gap + 2 lines of 20.48px, plus
   14px padding; a page hit's larger heading adds ~6px. The full text stays in
   the index and still matches. */
.md-search-result__article {
  box-sizing: border-box;
  max-height: 96px;
  padding-bottom: 14px;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 18px), transparent);
}
.md-search-result__article:has(> h1) {
  max-height: 102px;
}
/* A hit with nothing after its heading (a page title on its own) needs no
   fade, and the fade padding would make its hover fill sit low. */
.md-search-result__article:not(:has(> :not(h1, h2, .md-search-result__icon))) {
  padding-bottom: 0;
  -webkit-mask-image: none;
  mask-image: none;
}

/* Inline code in results: plain monospace, no chip. Three chips in a line of
   excerpt read as noise; on the page itself they stay. */
.md-search-result .md-typeset code {
  background: none;
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/* Matched terms: ink on a soft chip, instead of accent blue text. Material
   also underlines them; the chip is enough on its own. */
.md-search-result mark {
  color: var(--kn-ink);
  background: var(--kn-nav-active-bg);
  border-radius: 3px;
  padding: 0 2px;
  margin: 0 -2px;
  text-decoration: none;
}
.md-search-result__link:focus mark,
.md-search-result__link:hover mark {
  background: var(--kn-line-strong);
}

/* No per-result icon: every hit is a document, so the glyph said nothing.
   The text column moves left into the space it used. */
.md-search-result__icon {
  display: none;
}
/* Material sets the left padding under [dir=ltr], so the override has to
   carry the same prefix to outrank it. */
[dir="ltr"] .md-search-result__article,
[dir="ltr"] .md-search-result__meta,
[dir="ltr"] .md-search-result__more > summary > div {
  padding-left: 16px;
}

/* "N more on this page": muted text, and the same neutral fill as a hovered
   result, instead of link blue on the accent tint. */
.md-search-result__more > summary > div {
  color: var(--kn-mut);
}
.md-search-result__more > summary:focus > div,
.md-search-result__more > summary:hover > div {
  background-color: var(--kn-nav-active-bg);
  color: var(--kn-ink);
}



/* ── Responsive ─────────────────────────────────────────────────────────── */







/* ── Footer ─────────────────────────────────────────────────────────────────
   Two bands: Material's prev/next switcher on the page canvas, then the
   keln.ai site footer (ported from keln-website SiteFooter.astro). No meta
   bar — the "Made with Material" credit was dropped on the founder call
   2026-08-31; see the note in overrides/partials/footer.html.
   Footer values are copied from SiteFooter.astro, including its deliberate
   32px inline padding — narrower than .kwrap's 48px, per the note there. */

/* 1. Prev/next — Material paints this dark by default; here it belongs to the
      page, not the footer. */
.md-footer {
  background: none;
  color: var(--md-default-fg-color);
}
/* .md-footer__inner also carries .md-grid, so this needs to outrank the
   .md-grid padding set in the responsive section below. */
.md-footer .md-footer__inner {
  margin: 0 auto;
  padding: 8px 48px 64px;
  opacity: 1;
  gap: 16px;
}
.md-footer__link {
  padding-top: 24px;
  padding-bottom: 0;
  opacity: 1;
  border-radius: 10px;
}
.md-footer__title {
  font-size: 15px;
  line-height: 1.4;
}
.md-footer__direction {
  font-size: 12px;
  opacity: 0.65;
}
.md-footer__link:hover {
  color: var(--md-default-fg-color);
}

/* 2. The site footer. Always dark, as on keln.ai, in both colour schemes. */
.kfoot {
  background: #0a0a0a;
  color: #fff;
  border-top: 1px solid #1e1e1e;
  /* keln.ai's body sets 1.6 and every footer element inherits it. Material's
     body does not reach in here, so without this the wordmark, the column
     headings, the links and the base line all fall back to `normal` and the
     footer comes out 28px short of the real one. */
  line-height: 1.6;
}
.kfoot-inner {
  max-width: 1440px;
  margin: 0 auto;
  padding: 96px 32px 40px;
}
.kfoot-cols {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 32px;
}
.kfoot-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #fff;
}
.kfoot-brand img {
  display: block;
  width: 28px;
  height: 28px;
}
.kfoot-word {
  font-family: 'Geist Semibold', var(--md-text-font, inherit);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.045em;
}
.kfoot-tag {
  font-size: 13px;
  color: rgb(255 255 255 / 50%);
  margin: 14px 0 0;
  max-width: 280px;
  line-height: 1.55;
}
.kfoot-head {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 45%);
  margin: 0 0 14px;
}
.kfoot-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 13.5px;
  color: rgb(255 255 255 / 72%);
}
.kfoot-list a {
  color: inherit;
  display: block;
}
.kfoot-list a:hover {
  color: #fff;
}
.kfoot-base {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 44px;
  padding-top: 22px;
  border-top: 1px solid #1e1e1e;
  font-size: 12.5px;
  color: rgb(255 255 255 / 40%);
  flex-wrap: wrap;
  gap: 12px;
}
.kfoot-social {
  display: inline-flex;
  align-items: center;
  gap: 14px;
}
.kfoot-social a {
  display: inline-flex;
  opacity: 0.7;
  transition: opacity 0.15s;
}
.kfoot-social a:hover {
  opacity: 1;
}
.kfoot-social img {
  filter: brightness(0) invert(1);
}



/* ── Table of contents ──────────────────────────────────────────────────────
   The rail and the active marker are the SAME border, on the link itself. Drawn
   on the <ul> instead, the rail sat left of the links and you got two parallel
   lines. Width stays 2px in both states so the text never shifts. */
.md-sidebar--secondary .md-nav__title {
  font-size: 13px;
  font-weight: 400;
  color: var(--kn-mut);
  padding: 0 0 12px 16px;
  background: none;
  box-shadow: none;
}
.md-sidebar--secondary .md-nav__list {
  border-left: 0;
}
.md-sidebar--secondary .md-nav__item {
  padding: 0;
}
.md-sidebar--secondary .md-nav__link {
  display: block;
  margin: 0;
  padding: 6px 0 6px 16px;
  border-left: 2px solid var(--kn-line-strong);
  font-size: 14px;
  line-height: 1.45;
  color: var(--kn-mut);
  transition: color 0.15s, border-color 0.15s;
}
/* Sub-headings indent their text, not the rail — otherwise a second line. */
.md-sidebar--secondary .md-nav__list .md-nav__list {
  padding-left: 0;
}
.md-sidebar--secondary .md-nav__list .md-nav__list .md-nav__link {
  padding-left: 28px;
}
.md-sidebar--secondary .md-nav__link--active,
.md-sidebar--secondary .md-nav__link--active:hover {
  color: var(--kn-ink);
  font-weight: 500;
  border-left-color: var(--kn-ink);
}

/* ── Ink, not blue ──────────────────────────────────────────────────────────
   Material reaches for --md-typeset-a-color and --md-accent-fg-color for the
   active nav item, the sidebar scrollbar and buttons. Blue is right for a link
   in prose; on chrome it reads as "visited link" rather than "you are here". */

/* Left docs nav: the current PAGE only. An earlier version also matched
   .md-nav__item--active > .md-nav__link, which hits every ancestor section too
   and was half the reason the section labels had mismatched weights. */
.md-sidebar--primary .md-nav__link--active,
.md-sidebar--primary .md-nav__link--active:hover {
  color: var(--kn-ink);
  font-weight: 500;
}


/* Sidebar scrollbar — Material turns it accent-blue on hover/focus. */
.md-sidebar__scrollwrap,
.md-sidebar__scrollwrap:hover,
.md-sidebar__scrollwrap:focus-within {
  scrollbar-color: var(--kn-line-strong) transparent;
}
.md-sidebar__scrollwrap::-webkit-scrollbar-thumb,
.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover {
  background-color: var(--kn-line-strong);
}

/* Buttons — pill-shaped and ink, matching Button.astro. */
.md-typeset .md-button {
  /* Material ships these with margin:0, so a pair sits flush on desktop and
     the buttons touch edge-to-edge once they wrap onto separate lines. */
  margin: 0 10px 10px 0;
  color: var(--kn-ink);
  border-color: var(--kn-ink);
  border-width: 1.5px;
  border-radius: 999px;
  font-weight: 500;
}
.md-typeset .md-button:focus,
.md-typeset .md-button:hover {
  background-color: var(--kn-ink);
  border-color: var(--kn-ink);
  color: #fff;
}
.md-typeset .md-button--primary {
  background-color: var(--kn-btn-bg);
  border-color: var(--kn-btn-bg);
  color: var(--kn-btn-fg);
}
.md-typeset .md-button--primary:focus,
.md-typeset .md-button--primary:hover {
  background-color: var(--kn-btn-bg-hover);
  border-color: var(--kn-btn-bg-hover);
  color: var(--kn-btn-fg);
}

/* ── Theme switcher ─────────────────────────────────────────────────────────
   Material's radios live hidden in the header (they must exist before its
   anti-flash script runs); this is the control people click. Labels reach the
   radios by id, so :has() is what marks the checked one. */
.keln-header .md-header__option {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.kfoot-end {
  display: inline-flex;
  align-items: center;
  gap: 18px;
}
.kfoot-theme {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid #1e1e1e;
  border-radius: 999px;
}
.kfoot-theme__opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  color: rgb(255 255 255 / 45%);
  cursor: pointer;
  transition: color 0.15s, background-color 0.15s;
}
.kfoot-theme__opt svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentcolor;
  stroke-width: 1.5;
}
.kfoot-theme__opt:hover {
  color: #fff;
}
.kfoot-theme__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
body:has(#__palette_0:checked) .kfoot-theme__opt[for="__palette_0"],
body:has(#__palette_1:checked) .kfoot-theme__opt[for="__palette_1"],
body:has(#__palette_2:checked) .kfoot-theme__opt[for="__palette_2"] {
  color: #0a0a0a;
  background: #fff;
}

/* First visit: nothing stored, so Material checks nothing and the group would
   show no active segment. "System" is the effective state — mark it as such
   without writing a preference the reader never chose. */
body:not(:has(input[name="__palette"]:checked)) .kfoot-theme__opt[for="__palette_0"] {
  color: #0a0a0a;
  background: #fff;
}


/* ── Hover ──────────────────────────────────────────────────────────────────
   Everything in the chrome resolves to ink on hover. Collected here because it
   was six identical single-declaration rules; Material's own hover colour is
   --md-accent-fg-color, which reads as a link colour inside a menu. Hovers that
   do something OTHER than go ink (a background shift, opacity, a border) stay
   with their component. */
.knav-login:hover,
.knav-search-btn:hover,
.md-tabs__link:hover,
.keln-header .md-header__button.md-icon:hover,
.md-sidebar--primary .md-nav__link:hover,
.md-sidebar--primary .md-nav__link:focus,
.md-sidebar--secondary .md-nav__link:hover,
.md-sidebar--secondary .md-nav__link--passed:hover {
  color: var(--kn-ink);
}

/* ── Responsive ─────────────────────────────────────────────────────────────
   Every breakpoint in one place, narrowest rule last. Four thresholds, and they
   are not arbitrary:
     min 60em      (960px)  Material switches search from a fullscreen overlay
                            to the dropdown panel.
     max 76.1875em (1219px) Material turns the sidebar into a drawer — where the
                            burger has to appear and the sidebar search moves
                            back into the header.
     max 59.9375em (959px)  no room for the marketing links beside the burger.
     max 47.5em    (760px)  the footer's own breakpoint, copied from
                            SiteFooter.astro so it stacks exactly as on keln.ai.
   The 47.5em / 59.9375em pair is a deliberate inconsistency, not an oversight:
   the footer is ported and keeps the site's threshold.
   ──────────────────────────────────────────────────────────────────────────── */

/* Material's own search box normally sits inline in the header. The header is
   the marketing nav now, so hide it there and re-anchor the dialog under the
   header when it opens. Below 60em Material already renders it as a fullscreen
   overlay, which is what we want, so that is left alone.

   Deliberately POSITION ONLY. An earlier version also restyled the field
   (48px tall, 10px corners, bigger icons) and every one of those needed a
   follow-up fix, because Material sizes the results panel, the icon and the
   open-state radius against its own 36px field. Stock styling, moved. */








/* Material swaps the sidebar for a drawer at 76.1875em — that is where the
   hamburger has to appear, or the docs nav becomes unreachable. */

@media screen and (min-width: 60em) {
  .keln-header .md-search {
    position: absolute;
    /* +6px so the panel clears the header's bottom border instead of tucking
       underneath it. */
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    padding: 0;
  }
  .keln-header .md-search__inner {
    display: none;
    float: none;
    margin: 0 auto;
    padding: 0;
  }
  [data-md-toggle="search"]:checked ~ .keln-header .md-search__inner {
    display: block;
  }

  /* Corner radius only — 8px, matching the sidebar search trigger. Material
     re-declares this in the open state with a stronger selector, so both are
     needed. Everything else about the field stays stock. */
  .keln-header .md-search__form,
  [data-md-toggle="search"]:checked ~ .keln-header .md-search__form {
    border-radius: 8px 8px 0 0;
  }
  .keln-header .md-search__output {
    border-radius: 0 0 8px 8px;
  }
}

@media screen and (max-width: 76.1875em) {
  /* ── Drawer, Keln skin ────────────────────────────────────────────────────
     Colour and type only. Geometry is deliberately untouched: the header's
     5.6rem height and 3rem top padding are load-bearing for the sliding nested
     levels, and overriding the drawer's padding is what broke it earlier.

     Material fills the top-level header with --md-primary-fg-color at 700 via
     .md-nav__title[for=__drawer] — an attribute selector, so the plain class
     override loses to it. Both forms are listed here. */
  .md-nav--primary .md-nav__title,
  .md-nav--primary .md-nav__title[for="__drawer"] {
    background: var(--md-default-bg-color);
    color: var(--kn-ink);
    font-size: 15px;
    font-weight: 500;
    box-shadow: none;
  }
  /* Header and list share a background now, so the divider does the separating. */
  .md-nav--primary .md-nav__title ~ .md-nav__list {
    box-shadow: inset 0 1px 0 var(--kn-line);
  }
  /* Rows match the docked sidebar exactly: 14px. Material ships 16px. Note the
     row stays 48px tall — Material's padding is untouched — so the tap target
     is unaffected by the smaller type. */
  .md-nav--primary .md-nav__link {
    font-size: 14px;
    color: var(--kn-ink2);
  }
  .md-nav--primary .md-nav__link--active {
    color: var(--kn-ink);
    font-weight: 500;
  }

  .knav-burger {
    display: flex;
  }
  .knav-left {
    gap: 20px;
  }
  .md-grid {
    padding: 0 32px;
  }
  /* The sidebar is a drawer now and Material's nav paints over the trigger, so
     search moves back into the header as an icon. */
  .knav-search {
    display: none;
  }
  .knav-search-btn {
    display: flex;
  }
}

@media screen and (max-width: 59.9375em) {
  .md-grid {
    padding: 0 16px;
  }
  .knav-links,
  .knav-login {
    display: none;
  }
  .knav-word {
    font-size: 19px;
  }
}

@media screen and (max-width: 47.5em) {
  /* keln.ai hides the whole CTA cluster below 760px (SiteNav.astro) and moves
     Sign up into its burger panel. Our burger opens the docs nav instead, so
     here it simply drops out — which also matches the product: the platform
     is not usable on a phone yet (founder 2026-08-31). */
  .knav-signup {
    display: none;
  }

  .kfoot-inner {
    padding: 64px 20px 40px;
  }
  .kfoot-cols {
    grid-template-columns: 1fr 1fr;
    gap: 34px 18px;
  }
  .kfoot-cols > div:first-child {
    grid-column: 1 / -1;
  }
  .md-footer .md-footer__inner {
    padding: 8px 16px 32px;
  }
}

@media screen and (max-width: 22.5em) {
  .knav-word {
    display: none;
  }
}

/* ── Left sidebar, desktop only ─────────────────────────────────────────────
   Below 76.25em Material turns this sidebar into a slide-out drawer with its
   own padding, dividers and type scale. These rules are tuned for the docked
   sidebar; applied to the drawer they stripped its horizontal padding and left
   the labels colliding with the row dividers. Let the drawer keep Material's
   own styling. */
@media screen and (min-width: 76.25em) {
  /* Both sidebars open 24px down while the content column opens at 8px, so the
     search box and the table of contents sat below the H1. Match the content. */
  .md-sidebar {
    padding-top: 8px;
  }

  /* Section labels, normalised. Material weights these 700, 500 or 400 depending
     on nesting depth and on whether the section contains the current page, which
     reads as random. One treatment for all of them. */
  .md-sidebar--primary .md-nav__item--section > label.md-nav__link,
  .md-sidebar--primary .md-nav__item--section > .md-nav__link:not([href]) {
    font-size: 13px;
    font-weight: 500;
    letter-spacing: -0.01em;
    color: var(--kn-mut);
    cursor: default;
    padding-bottom: 2px;
  }

  /* ── Left nav rhythm ────────────────────────────────────────────────────────
     Material packs the list at a 27px pitch around an 18px row with no padding,
     so the rows nearly touch and the eye has nothing to step between. Real
     vertical padding takes the pitch to ~34px and doubles as a bigger hit area.
     Font stays 14px, matching the site header's nav. */
  .md-sidebar--primary .md-nav__link {
    margin: 0;
    padding: 7px 0;
    line-height: 1.45;
  }
  /* Breathing room between groups, so sections read as groups. */
  .md-sidebar--primary .md-nav__item--section {
    margin-top: 26px;
  }
  .md-sidebar--primary .md-nav__list > .md-nav__item--section:first-child {
    margin-top: 0;
  }
}
