/* =========================================================================
   dm-champ-docs-generator — base theme
   Light mode only in v0.1. Tunable via CSS variables set at build time.
   ========================================================================= */

:root {
  /* Brand — overridden per-tenant via <style> in <head> */
  --brand-primary: #25d366;
  --brand-accent: #128c7e;
  --brand-font: "Inter", system-ui, -apple-system, sans-serif;
  --brand-font-display: "Space Grotesk", var(--brand-font);
  --brand-font-mono:
    "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Surfaces */
  --surface-0: #ffffff;
  --surface-1: #fafbfc;
  --surface-2: #f4f6f8;
  --surface-3: #eef1f4;

  /* Text */
  --text-strong: #0b1020;
  --text-body: #1f2937;
  --text-muted: #5c6373;
  --text-subtle: #8a93a6;

  /* Borders */
  --border-subtle: rgba(15, 23, 42, 0.06);
  --border-default: rgba(15, 23, 42, 0.1);
  --border-strong: rgba(15, 23, 42, 0.16);

  /* Radii */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-xl: 20px;
  --r-pill: 999px;

  /* Elevation */
  --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.04);
  --shadow-sm:
    0 1px 2px rgba(15, 23, 42, 0.06), 0 2px 8px rgba(15, 23, 42, 0.04);
  --shadow-md:
    0 4px 16px rgba(15, 23, 42, 0.06), 0 12px 36px rgba(15, 23, 42, 0.04);

  /* Layout */
  --topbar-h: 72px;
  --sidebar-w: 272px;
  --toc-w: 220px;
  --content-max: 780px;

  /* Motion */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
html,
body {
  margin: 0;
  padding: 0;
}
html {
  scroll-behavior: smooth;
}

/* The runtime feature gate (APPLY_BRAND_SCRIPT) hides nav links and other
   gated elements by setting the `hidden` attribute. Browsers ship a UA rule
   `[hidden] { display: none }` for that, but our component rules
   (.topnav-link → display: inline-flex, .nav-item > a → display: flex)
   have higher specificity and would override it — leaving the gated link
   visible. Reasserting !important here keeps the gate authoritative. */
[hidden] {
  display: none !important;
}

body {
  font-family: var(--brand-font);
  font-size: 16px;
  line-height: 1.7;
  color: var(--text-body);
  background: var(--surface-0);
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Grid backdrop — soft radial mask keeps content legible while giving the
   page texture. Fixed so it stays put on scroll. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(rgba(15, 23, 42, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(15, 23, 42, 0.055) 1px, transparent 1px);
  background-size: 52px 52px;
  -webkit-mask-image: radial-gradient(
    ellipse 90% 80% at 50% 20%,
    black 10%,
    transparent 85%
  );
  mask-image: radial-gradient(
    ellipse 90% 80% at 50% 20%,
    black 10%,
    transparent 85%
  );
  pointer-events: none;
  z-index: 0;
}

/* Soft tint that picks up the brand color behind the hero area. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  background:
    radial-gradient(
      ellipse 60% 40% at 50% -5%,
      color-mix(in srgb, var(--brand-primary) 9%, transparent) 0%,
      transparent 60%
    ),
    radial-gradient(
      ellipse 40% 30% at 85% 10%,
      color-mix(in srgb, var(--brand-accent) 7%, transparent) 0%,
      transparent 60%
    );
  pointer-events: none;
  z-index: 0;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background: rgba(15, 23, 42, 0.15);
  border-radius: 999px;
  border: 2px solid var(--surface-0);
}
::-webkit-scrollbar-thumb:hover {
  background: rgba(15, 23, 42, 0.28);
}

::selection {
  background: color-mix(in srgb, var(--brand-primary) 22%, transparent);
}

a {
  color: var(--brand-primary);
  text-decoration: none;
  transition: color 0.15s var(--ease);
}
a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* =========================================================================
   Layout
   ========================================================================= */

.layout {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 1;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: var(--topbar-h);
  padding: 0 28px;
  background: rgba(255, 255, 255, 0.75);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--border-subtle);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-strong);
  font-family: var(--brand-font-display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.02em;
}

.brand:hover {
  text-decoration: none;
}

/* CSS-painted logo (was an <img>). The image is fetched as a CSS background
   instead of via a replaced element, which avoids the per-navigation flicker
   you get when an <img> is created with src="" and only swapped to the real
   URL once JS runs. --brand-logo-url is set synchronously in the head script
   and combined with a <link rel="preload"> so the image is in cache before
   the body paints. Fixed width (140px) reserves layout so neighboring
   elements never reflow when the image arrives. */
.brand-logo {
  display: inline-block;
  /* Tenant logo size: --brand-logo-h/-w are set from the brand manifest's
     logoSize (26px/140px × logoSize/34, see logoGeometry in types.ts); unset
     for tenants on the default size. Capped so a 72px setting (55×296) never
     outgrows the sticky topbar. */
  height: var(--brand-logo-h, 26px);
  width: var(--brand-logo-w, 140px);
  max-height: 56px;
  max-width: min(300px, 45vw);
  background-image: var(--brand-logo-url, none);
  background-repeat: no-repeat;
  background-position: left center;
  background-size: contain;
}
/* Neutral root (docs.youraiconnector.com with no tenant hint): no brand, so
   no app to open. Set pre-paint by PRE_BRAND_SCRIPT, reconciled by
   APPLY_BRAND_SCRIPT once the brand resolves. */
html[data-neutral-app] .topbar-link {
  display: none;
}

html[data-no-logo] .brand-logo {
  display: none;
}

/* Brand-name visibility is gated by an html attribute set synchronously in
   the head script BEFORE the brand-name element is parsed. A reactive sibling
   selector (.brand-logo:not([hidden]) ~ .brand-name) would only update after
   the MutationObserver removes `hidden`, one microtask too late, producing a
   visible "AppName Docs" flicker between paint and swap on cached nav. */
.brand-name {
  white-space: nowrap;
  display: none;
}
html[data-no-logo] .brand-name {
  display: inline;
}

.topnav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  margin-left: 24px;
}

.topnav-link {
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  transition:
    background 0.12s var(--ease),
    color 0.12s var(--ease);
}

.topnav-link:hover {
  background: var(--surface-2);
  color: var(--text-strong);
  text-decoration: none;
}

.topnav-link-active {
  color: var(--brand-primary);
  background: color-mix(in srgb, var(--brand-primary) 10%, transparent);
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Hamburger toggle — visible only on mobile (see responsive block below). */
.sidebar-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-default);
  border-radius: var(--r-sm);
  color: var(--text-strong);
  cursor: pointer;
}
.sidebar-toggle:hover {
  background: var(--surface-2);
}
.sidebar-toggle svg {
  width: 20px;
  height: 20px;
}

/* Backdrop behind the off-canvas sidebar on mobile. Hidden by default; the
   drawer script unhides it and toggles `.is-visible` for the fade-in. */
.sidebar-backdrop {
  display: none;
}

/* In-drawer header (logo + close button) and mobile space links. Hidden on
   desktop; the responsive block reveals them. */
.sidebar-header {
  display: none;
}
.sidebar-spaces {
  display: none;
}

/* OpenAPI pages render the drawer outside `.shell` (Scalar owns the desktop
   nav). Hide it entirely on desktop — the mobile media query overrides this
   so the drawer still works on small screens. */
.sidebar-mobile-only {
  display: none;
}

/* ===== Search trigger — reads like a filled input even though it's a button ===== */
.search-trigger {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: 280px;
  height: 36px;
  padding: 0 10px 0 12px;
  background: var(--surface-1);
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
  color: var(--text-subtle);
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  text-align: left;
  cursor: pointer;
  transition:
    background 0.12s var(--ease),
    border-color 0.12s var(--ease),
    color 0.12s var(--ease),
    box-shadow 0.12s var(--ease);
}

.search-trigger:hover {
  background: var(--surface-0);
  border-color: var(--border-strong);
  color: var(--text-body);
  box-shadow: var(--shadow-xs);
}

.search-trigger:focus-visible {
  outline: none;
  border-color: var(--brand-primary);
  box-shadow: 0 0 0 3px
    color-mix(in srgb, var(--brand-primary) 18%, transparent);
}

.search-trigger-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--text-subtle);
}

.search-trigger-label {
  flex: 1;
  font-weight: 400;
}

.search-trigger-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 22px;
  padding: 0 6px;
  font-family: var(--brand-font);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-subtle);
  background: var(--surface-0);
  border: 1px solid var(--border-default);
  border-radius: 5px;
  box-shadow: inset 0 -1px 0 rgba(15, 23, 42, 0.06);
}

@media (max-width: 1100px) {
  .search-trigger {
    width: 220px;
  }
}

@media (max-width: 820px) {
  .search-trigger {
    width: 36px;
    padding: 0;
    justify-content: center;
  }
  .search-trigger-label,
  .search-trigger-kbd {
    display: none;
  }
}

/* ===== Search modal ===== */
.search-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: min(12vh, 96px) 20px 20px;
}

.search-modal[hidden] {
  display: none;
}

.search-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(11, 16, 32, 0.45);
  backdrop-filter: blur(6px) saturate(160%);
  -webkit-backdrop-filter: blur(6px) saturate(160%);
  animation: searchBackdropIn 0.18s var(--ease);
}

.search-modal-panel {
  position: relative;
  width: 100%;
  max-width: 640px;
  background: var(--surface-0);
  border: 1px solid var(--border-default);
  border-radius: var(--r-lg);
  box-shadow:
    0 20px 50px rgba(15, 23, 42, 0.25),
    0 6px 16px rgba(15, 23, 42, 0.08);
  overflow: hidden;
  animation: searchPanelIn 0.22s var(--ease);
}

@keyframes searchBackdropIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes searchPanelIn {
  from {
    opacity: 0;
    transform: translateY(-8px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Pagefind UI — theme to match our CSS vars. */
.search-modal-panel .pagefind-ui {
  --pagefind-ui-primary: var(--brand-primary);
  --pagefind-ui-text: var(--text-body);
  --pagefind-ui-background: var(--surface-0);
  --pagefind-ui-border: var(--border-subtle);
  --pagefind-ui-tag: var(--surface-2);
  --pagefind-ui-border-width: 1px;
  --pagefind-ui-border-radius: 10px;
  --pagefind-ui-font: var(--brand-font);
  padding: 10px 14px 14px;
}

.search-modal-panel .pagefind-ui__form::before {
  background-color: var(--text-subtle) !important;
  opacity: 0.85;
}

.search-modal-panel .pagefind-ui__search-input {
  font-size: 16px;
  font-family: var(--brand-font);
  background: transparent;
  border: none;
  padding: 14px 14px 14px 40px;
  border-bottom: 1px solid var(--border-subtle);
  border-radius: 0;
  color: var(--text-strong);
}

.search-modal-panel .pagefind-ui__search-input:focus {
  outline: none;
  box-shadow: none;
}

.search-modal-panel .pagefind-ui__search-clear {
  color: var(--text-subtle);
}

.search-modal-panel .pagefind-ui__results-area {
  max-height: min(60vh, 520px);
  overflow-y: auto;
  padding: 8px 2px;
}

.search-modal-panel .pagefind-ui__message {
  font-size: 13px;
  color: var(--text-subtle);
  padding: 14px 16px;
}

.search-modal-panel .pagefind-ui__result {
  border-radius: 10px;
  border: 1px solid transparent;
  padding: 12px 14px;
  margin: 4px 0;
  transition:
    background 0.12s var(--ease),
    border-color 0.12s var(--ease);
}

.search-modal-panel .pagefind-ui__result:hover {
  background: var(--surface-1);
  border-color: var(--border-subtle);
}

.search-modal-panel .pagefind-ui__result-title {
  font-family: var(--brand-font-display);
  font-weight: 600;
  font-size: 15px;
  color: var(--text-strong);
}

.search-modal-panel .pagefind-ui__result-excerpt {
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.55;
}

.search-modal-panel .pagefind-ui__result-excerpt mark {
  background: color-mix(in srgb, var(--brand-primary) 22%, transparent);
  color: var(--text-strong);
  border-radius: 3px;
  padding: 0 2px;
}

body.page-openapi .search-trigger {
  display: none;
}

.topbar-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 36px;
  padding: 0 18px;
  border-radius: var(--r-pill);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: #ffffff;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--brand-primary) 92%, white) 0%,
    var(--brand-primary) 100%
  );
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 -1px 0 rgba(0, 0, 0, 0.1),
    0 1px 2px rgba(0, 0, 0, 0.12),
    0 4px 12px color-mix(in srgb, var(--brand-primary) 28%, transparent);
  transition:
    transform 0.15s var(--ease),
    box-shadow 0.15s var(--ease),
    filter 0.15s var(--ease);
}

.topbar-link:hover {
  text-decoration: none;
  filter: brightness(1.08);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.26),
    inset 0 -1px 0 rgba(0, 0, 0, 0.1),
    0 2px 4px rgba(0, 0, 0, 0.12),
    0 8px 24px color-mix(in srgb, var(--brand-primary) 34%, transparent);
}

.topbar-link:active {
  transform: scale(0.98);
  filter: brightness(0.95);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.2),
    0 1px 2px rgba(0, 0, 0, 0.15);
}

.shell {
  flex: 1;
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  max-width: 1480px;
  width: 100%;
  margin: 0 auto;
}

/* =========================================================================
   Sidebar
   ========================================================================= */

.sidebar {
  padding: 28px 12px 48px 24px;
  position: sticky;
  top: var(--topbar-h);
  align-self: start;
  max-height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  scrollbar-gutter: stable;
  font-size: 14px;
}

.nav-section + .nav-section {
  margin-top: 22px;
}

.nav-section-title {
  font-family: var(--brand-font-display);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-subtle);
  font-weight: 600;
  padding: 0 10px 10px;
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-list .nav-list {
  margin: 2px 0 4px 10px;
  padding-left: 8px;
  border-left: 1px solid var(--border-subtle);
}

.nav-item > a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: var(--r-sm);
  color: var(--text-muted);
  line-height: 1.45;
  transition:
    background 0.12s var(--ease),
    color 0.12s var(--ease);
}

.nav-item > a:hover {
  background: var(--surface-2);
  color: var(--text-strong);
  text-decoration: none;
}

.nav-item-active > a {
  background: color-mix(in srgb, var(--brand-primary) 10%, transparent);
  color: var(--brand-primary);
  font-weight: 500;
}

.nav-icon {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  color: var(--text-subtle);
  transition: color 0.12s var(--ease);
}
.nav-item > a:hover .nav-icon {
  color: var(--text-strong);
}
.nav-item-active > a .nav-icon {
  color: var(--brand-primary);
}
.nav-item-label {
  min-width: 0;
}
/* Nested items get a slightly tighter icon gap than top level. (They used to
   render icon-less with gap: 0, but the v2 tree gives children icons too —
   gap: 0 smashed the icon into the label.) */
.nav-list .nav-list .nav-item > a {
  gap: 8px;
}

/* =========================================================================
   Content
   ========================================================================= */

.content {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--toc-w);
  gap: 56px;
  padding: 48px 56px 96px;
}

.page {
  min-width: 0;
  max-width: var(--content-max);
}

.page h1,
.page h2,
.page h3,
.page h4 {
  font-family: var(--brand-font-display);
  color: var(--text-strong);
  text-wrap: balance;
  letter-spacing: -0.025em;
  /* Anchor link for each heading follows the heading text in markup; pulling
     it out of flow via `position: absolute` parks it in the left margin, so
     the heading stays flush-left with body copy instead of visibly indented. */
  position: relative;
}

.page h1 {
  font-size: 2.5rem;
  font-weight: 700;
  line-height: 1.1;
  margin: 0 0 0.6em;
  background: linear-gradient(
    180deg,
    var(--text-strong) 0%,
    color-mix(in srgb, var(--text-strong) 70%, var(--brand-primary)) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.page h2 {
  font-size: 1.55rem;
  font-weight: 650;
  line-height: 1.25;
  margin: 2.25em 0 0.5em;
  scroll-margin-top: calc(var(--topbar-h) + 16px);
}

.page h3 {
  font-size: 1.18rem;
  font-weight: 600;
  line-height: 1.35;
  margin: 1.75em 0 0.5em;
  scroll-margin-top: calc(var(--topbar-h) + 16px);
}

.page h4 {
  font-size: 1rem;
  font-weight: 600;
  margin: 1.5em 0 0.4em;
}

.page p,
.page ul,
.page ol {
  margin: 0 0 1.1em;
}

.page ul,
.page ol {
  padding-left: 1.4em;
}
.page li {
  margin-bottom: 0.35em;
}
.page li::marker {
  color: var(--text-subtle);
}

.page strong {
  color: var(--text-strong);
  font-weight: 600;
}

.page hr {
  border: none;
  border-top: 1px solid var(--border-default);
  margin: 2.5em 0;
}

/* Inline code */
.page :not(pre) > code {
  font-family: var(--brand-font-mono);
  font-size: 0.88em;
  background: var(--surface-2);
  color: var(--text-strong);
  padding: 0.15em 0.4em;
  border-radius: 5px;
  border: 1px solid var(--border-subtle);
  /* A long token (an endpoint URL, a header name) has no break opportunity,
     so on a phone it ran past the viewport and made the whole page pan
     sideways. Let it break mid-token rather than widen the document.
     Fenced blocks are excluded — they scroll inside their own `pre`. */
  overflow-wrap: anywhere;
}

/* Fenced code blocks (shiki) */
.page pre {
  position: relative;
  margin: 1.25em 0;
  padding: 18px 20px;
  border-radius: var(--r-md);
  overflow-x: auto;
  background: #f6f8fa !important;
  border: 1px solid var(--border-default);
  box-shadow: var(--shadow-xs);
  font-size: 0.88rem;
  line-height: 1.65;
}

.page pre code {
  background: transparent;
  border: none;
  padding: 0;
  font-family: var(--brand-font-mono);
  color: inherit;
}

.page pre .shiki {
  background: transparent !important;
}

/* Shiki dual-theme renders each span with `--shiki-light` / `--shiki-dark`
   custom props but no `color` declaration — it expects the consumer to map
   one variant to `color`. Map the light-theme foreground; without this,
   tokens with no inline style (and the wrapper `<pre>` itself) inherit
   the page text color. */
.page pre.shiki,
.page pre.shiki span {
  color: var(--shiki-light);
}

/* Headings anchor icon (markdown-it-anchor ariaHidden) — positioned in the
   left margin without taking flow space, so the heading text stays flush
   with body copy. */
.heading-anchor {
  position: absolute;
  right: 100%;
  padding-right: 8px;
  color: var(--text-subtle);
  opacity: 0;
  transition: opacity 0.15s var(--ease);
  font-weight: 400;
  text-decoration: none;
}
.page h1:hover .heading-anchor,
.page h2:hover .heading-anchor,
.page h3:hover .heading-anchor,
.page h4:hover .heading-anchor {
  opacity: 1;
}
.page h1:hover .heading-anchor {
  -webkit-text-fill-color: var(--text-subtle);
}
/* The left margin only exists at the wide content padding. Narrow layouts
   (and touch devices, where a tap leaves :hover stuck on) rendered the "#"
   half off-screen against the content edge. There, drop the out-of-flow
   positioning and let it sit inline at the end of the heading — it already
   follows the text in the DOM. */
@media (hover: none), (max-width: 820px) {
  .heading-anchor {
    position: static;
    padding-right: 0;
    padding-left: 0.3em;
    opacity: 0.55;
    font-size: 0.85em;
  }
  /* h1 paints its text through a clipped gradient with a transparent fill;
     the anchor inherits that and would render invisible. */
  .page h1 .heading-anchor {
    -webkit-text-fill-color: var(--text-subtle);
  }
}

/* Tables — every one is wrapped in `.table-wrap` by the renderer so a table
   too wide for the viewport scrolls inside its own box instead of widening
   the document and letting the whole page pan sideways on mobile. The
   wrapper carries the vertical rhythm; the table itself has no margin. */
.table-wrap {
  margin: 1.25em 0;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* Without this the table's own radius+shadow get clipped square by the
     scroll box's padding edge. */
  padding: 1px;
}

.page table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  margin: 0;
  font-size: 0.95em;
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-xs);
}

.page th,
.page td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-subtle);
  text-align: left;
  vertical-align: top;
}
.page tr:last-child td {
  border-bottom: none;
}

.page th {
  background: var(--surface-1);
  font-weight: 600;
  font-size: 0.85em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.page tbody tr:hover td {
  background: var(--surface-1);
}

/* Blockquotes (fallback — most are upgraded to callouts) */
.page blockquote {
  margin: 1.25em 0;
  padding: 12px 18px;
  background: var(--surface-1);
  border-left: 3px solid var(--brand-primary);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--text-muted);
}
.page blockquote p:last-child {
  margin-bottom: 0;
}

/* Images */
.page img {
  max-width: 100%;
  border-radius: var(--r-md);
  border: 1px solid var(--border-default);
  box-shadow: var(--shadow-sm);
}

/* Click-to-zoom lightbox (script lives in the page template). Figures get the
   zoom affordance; the overlay shows the retina capture near full size. */
.page figure img {
  cursor: zoom-in;
}
.img-lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vh 3vw;
  background: rgba(15, 23, 42, 0.82);
  backdrop-filter: blur(4px);
  cursor: zoom-out;
}
.img-lightbox img {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--r-md);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
}

/* Live tenant figures (LIVE_SNAPSHOT_SCRIPT in templates/page.ts). On a
   white-label docs domain a figure's PNG is replaced by a themed DOM snapshot
   of the same screen, rendered in a sandboxed iframe that keeps its captured
   1440x900 pixel size and is scaled down by JS. The wrapper carries the
   resulting height so the page flows exactly as it did around the image, and
   clips the scaled frame. The frame is inert: no scripts (CSP + sandbox), no
   focus (tabindex -1), no pointer events. */
.live-figure {
  position: relative;
  display: block;
  width: 100%;
  /* No-JS / pre-JS default: the rebrander swaps figures to this markup
     server-side, so the wrapper must already reserve the capture's shape and
     clip the un-scaled frame to a top-left crop before any script runs. JS
     replaces the height with 900 * scale and scales the frame to fit. */
  aspect-ratio: 1440 / 900;
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px solid var(--border-default);
  box-shadow: var(--shadow-sm);
  background: var(--surface-1);
}
.live-figure iframe,
.live-figure__frame {
  display: block;
  border: 0;
  width: 1440px;
  height: 900px;
  transform-origin: top left;
  pointer-events: none;
}
/* Inside the walkthrough stage the frame IS the stage: the player draws its
   own border/background, and the hotspot sits on top of it. */
/* Stage placeholder while the player decides between PNG and live snapshots
   (nothing is attached in that window — see startVisuals). */
.wt-imgwrap.wt-pending {
  aspect-ratio: 1440 / 900;
}

.wt-liveframe {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  margin: 0 auto;
}

/* =========================================================================
   Interactive walkthrough player (`::: walkthrough <id>`, mount markup from
   renderPage.ts, behavior in templates/page.ts WALKTHROUGH_SCRIPT). Uses the
   same surface/border/shadow variables as the rest of the theme so it stays
   in step with any future theme retune — no colors are hardcoded here.
   ========================================================================= */

.walkthrough-embed {
  margin: 1.75em 0;
}

.wt-player {
  border: 1px solid var(--border-default);
  border-radius: var(--r-lg);
  background: var(--surface-1);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.wt-stage {
  position: relative;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-default);
}

.wt-imgwrap {
  position: relative;
  line-height: 0;
}

.wt-img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0;
  border: 0;
  box-shadow: none;
}

/* Click bubble — positioned in px by JS from the flow's relative hotspot
   coordinates, scaled to the rendered image box (retina/resized art stays
   accurate without a build-time image-dimension dependency). */
.wt-hotspot {
  position: absolute;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.wt-hotspot-ring,
.wt-hotspot-dot {
  position: absolute;
  inset: 0;
  border-radius: 50%;
}
.wt-hotspot-ring {
  background: color-mix(in srgb, var(--brand-primary) 32%, transparent);
  animation: wtPulse 1.8s var(--ease) infinite;
}
.wt-hotspot-dot {
  inset: 32%;
  background: var(--brand-primary);
  box-shadow: 0 0 0 2px color-mix(in srgb, white 88%, var(--brand-primary) 12%);
}
.wt-hotspot:focus-visible .wt-hotspot-dot {
  outline: 2px solid var(--brand-primary);
  outline-offset: 3px;
}

@keyframes wtPulse {
  0% {
    transform: scale(0.55);
    opacity: 0.9;
  }
  70%,
  100% {
    transform: scale(1.55);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wt-hotspot-ring {
    animation: none;
    opacity: 0.5;
  }
}

.wt-caption {
  margin: 0;
  padding: 14px 16px 4px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--text-body);
}

.wt-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px 14px;
}
/* Phones (and the app's help modal, whose iframe is phone-wide): five
   32px buttons + a 10-dot progress strip need ~370px, so the row used to
   spill past the player's right edge and the fullscreen button got
   clipped. Give the dots their own line above the buttons instead. */
@media (max-width: 480px) {
  .wt-controls {
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 12px;
  }
  /* Compound selector: the base `.wt-dots` rule below this block would
     otherwise win on source order. */
  .wt-controls .wt-dots {
    flex: 1 1 100%;
    order: -1;
    flex-wrap: wrap;
  }
}

.wt-nav,
.wt-sound,
.wt-replay,
.wt-fs {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--border-default);
  border-radius: var(--r-pill);
  background: var(--surface-0);
  color: var(--text-body);
  cursor: pointer;
  transition:
    background 0.12s var(--ease),
    border-color 0.12s var(--ease),
    color 0.12s var(--ease);
}
.wt-nav svg,
.wt-sound svg,
.wt-replay svg,
.wt-fs svg {
  width: 16px;
  height: 16px;
}
.wt-nav:hover:not(:disabled),
.wt-sound:hover,
.wt-replay:hover,
.wt-fs:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}
.wt-nav:disabled {
  opacity: 0.35;
  cursor: default;
}
.wt-sound[aria-pressed="true"],
.wt-fs[aria-pressed="true"] {
  color: var(--brand-primary);
  border-color: color-mix(
    in srgb,
    var(--brand-primary) 40%,
    var(--border-default)
  );
}

.wt-leadin {
  margin: 0 0 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-body);
}

/* Poster/start state: a scrim + big Play circle over step 1. Nothing
   advances or speaks until this is clicked (also satisfies the browser's
   user-gesture requirement for audio). */
.wt-start {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border: 0;
  padding: 0;
  cursor: pointer;
  background: color-mix(in srgb, var(--surface-0) 42%, transparent);
  backdrop-filter: blur(2px);
  color: var(--text-body);
}
.wt-start-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--brand-primary);
  color: #fff;
  box-shadow: 0 6px 24px
    color-mix(in srgb, var(--brand-primary) 45%, transparent);
  transition: transform 0.15s var(--ease);
}
.wt-start:hover .wt-start-circle {
  transform: scale(1.07);
}
.wt-start-circle svg {
  width: 30px;
  height: 30px;
}
.wt-start-label {
  font-size: 14px;
  font-weight: 600;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: var(--surface-0);
  border: 1px solid var(--border-default);
}

/* Fullscreen: the player element itself is the fullscreen root. Fill the
   screen, keep the stage centered, and let the image scale to the available
   height instead of its inline width. */
.wt-player.wt-fullscreen-active {
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: var(--surface-0);
  padding: 24px;
}
.wt-player.wt-fullscreen-active .wt-stage {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.wt-player.wt-fullscreen-active .wt-imgwrap {
  max-height: 100%;
}
.wt-player.wt-fullscreen-active .wt-img {
  max-height: calc(100vh - 170px);
  width: auto;
  max-width: 100%;
}
/* In fullscreen the inline caption reads tiny and lost at the bottom left —
   restyle it as video-style subtitles: centered over the image on a dark
   scrim, sized for a room-distance read. White-on-dark works in both themes. */
.wt-player.wt-fullscreen-active {
  position: relative;
}
.wt-player.wt-fullscreen-active .wt-caption {
  position: absolute;
  left: 50%;
  bottom: 86px;
  transform: translateX(-50%);
  z-index: 2;
  max-width: min(70ch, 82vw);
  padding: 12px 22px;
  border-radius: 12px;
  text-align: center;
  font-size: clamp(18px, 2.2vw, 26px);
  line-height: 1.5;
  font-weight: 500;
  color: #fff;
  background: rgba(10, 12, 16, 0.68);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(3px);
  pointer-events: none;
}

.wt-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 1 1 auto;
}
.wt-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--border-strong);
  cursor: pointer;
  transition:
    background 0.15s var(--ease),
    transform 0.15s var(--ease);
}
.wt-dot:hover {
  background: color-mix(
    in srgb,
    var(--brand-primary) 45%,
    var(--border-strong)
  );
}
.wt-dot.is-active {
  background: var(--brand-primary);
  transform: scale(1.25);
}

/* =========================================================================
   Changelog jump bar — auto-injected at the top of the aggregated changelog
   so visitors can hop to a month without scrolling. The right-rail TOC
   already shows this on desktop ≥1101px; this duplicate exists for the
   smaller viewports where the rail is hidden.
   ========================================================================= */

.changelog-jump {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 24px 0 32px;
  padding: 12px 14px;
  background: var(--surface-1);
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
}

.changelog-jump-label {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
  margin-right: 4px;
}

.changelog-jump-link {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-0);
  border: 1px solid var(--border-default);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
  white-space: nowrap;
  transition:
    background 0.12s var(--ease),
    color 0.12s var(--ease),
    border-color 0.12s var(--ease);
}

.changelog-jump-link:hover {
  background: color-mix(in srgb, var(--brand-primary) 8%, transparent);
  border-color: color-mix(
    in srgb,
    var(--brand-primary) 35%,
    var(--border-default)
  );
  color: var(--text-strong);
  text-decoration: none;
}

/* =========================================================================
   Callouts (from markdown-it-container)
   ========================================================================= */

.callout {
  --cal-color: var(--brand-primary);
  position: relative;
  margin: 1.5em 0;
  padding: 16px 18px 16px 48px;
  border-radius: var(--r-md);
  background: var(--surface-1);
  border: 1px solid var(--border-default);
  box-shadow: var(--shadow-xs);
}

.callout::before {
  content: "";
  position: absolute;
  left: 16px;
  top: 18px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--cal-color);
  opacity: 0.15;
}

.callout::after {
  content: "";
  position: absolute;
  left: 22px;
  top: 24px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--cal-color);
}

.callout p:last-child {
  margin-bottom: 0;
}
.callout strong {
  color: var(--text-strong);
}

.callout-info,
.callout-note {
  --cal-color: #3b82f6;
}
.callout-tip {
  --cal-color: #10b981;
}
.callout-warning {
  --cal-color: #f59e0b;
}
.callout-danger {
  --cal-color: #ef4444;
}

/* =========================================================================
   TOC (right rail)
   ========================================================================= */

.toc {
  position: sticky;
  top: calc(var(--topbar-h) + 16px);
  align-self: start;
  max-height: calc(100vh - var(--topbar-h) - 32px);
  overflow-y: auto;
  font-size: 13px;
  padding-left: 12px;
  border-left: 1px solid var(--border-subtle);
}

.toc-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--brand-font-display);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-subtle);
  font-weight: 600;
  margin-bottom: 12px;
  padding-left: 2px;
}
.toc-title svg {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
}

.toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.toc-item a {
  display: block;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  color: var(--text-muted);
  line-height: 1.45;
  transition:
    background 0.12s var(--ease),
    color 0.12s var(--ease);
}

.toc-item a:hover {
  background: var(--surface-2);
  color: var(--text-strong);
  text-decoration: none;
}

.toc-item.is-active > a {
  background: color-mix(in srgb, var(--brand-primary) 10%, transparent);
  color: var(--brand-primary);
  font-weight: 500;
}

.toc-level-3 a {
  padding-left: 22px;
  font-size: 12px;
}

/* =========================================================================
   OpenAPI / Scalar
   ========================================================================= */

/* OpenAPI pages go edge-to-edge — Scalar owns the full width and brings
   its own left sidebar. */
.shell-openapi {
  grid-template-columns: 1fr;
  max-width: none;
}

.content-openapi {
  display: block;
  gap: 0;
  padding: 0;
}

/* The openapi page's own markdown body (api/reference.md) is a short preface
   above the explorer: same measure and type as an ordinary page, then a rule
   before Scalar takes over the full width. */
.page-openapi-intro {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 40px 24px 28px;
  border-bottom: 1px solid var(--border-default);
}

.page-openapi-intro h1 {
  margin-top: 0;
}

body.page-openapi::before,
body.page-openapi::after {
  display: none;
}

.api-reference-wrapper .dark-mode-toggle,
.api-reference-wrapper [data-testid="theme-picker"],
.api-reference-wrapper .scalar-app-layout .darklight,
.api-reference-wrapper .darklight-reference,
.api-reference-wrapper .t-doc__sidebar-theme-toggle {
  display: none !important;
}

/* Scalar's mobile menu / settings popover only contains items we already
   hide above (theme picker, dark-mode toggle), so on mobile the trigger
   button opens an empty popover. Hide the trigger and any mobile header
   chrome it sits in — our docs drawer + the API ref scroll body cover
   navigation. */
.api-reference-wrapper .t-doc__header-mobile,
.api-reference-wrapper .scalar-app-layout-mobile-header,
.api-reference-wrapper .scalar-mobile-header,
.api-reference-wrapper [class*="mobile-header"],
.api-reference-wrapper [class*="header-mobile"],
.api-reference-wrapper button[aria-label*="settings" i].scalar-icon-button,
.api-reference-wrapper button[aria-label*="menu" i].scalar-icon-button,
.api-reference-wrapper button[aria-label*="more" i].scalar-icon-button {
  display: none !important;
}

.api-reference-wrapper {
  --scalar-color-accent: var(--brand-primary);
  --scalar-color-1: var(--text-strong);
  --scalar-color-2: var(--text-body);
  --scalar-color-3: var(--text-muted);
  --scalar-background-1: var(--surface-0);
  --scalar-background-2: var(--surface-1);
  --scalar-background-3: var(--surface-2);
  --scalar-border-color: var(--border-default);
  --scalar-radius: var(--r-md);
  --scalar-radius-lg: var(--r-lg);
  --scalar-font: var(--brand-font);
  --scalar-font-code: var(--brand-font-mono);
  --scalar-button-1: var(--brand-primary);
  --scalar-button-1-color: #ffffff;
  --scalar-button-1-hover: color-mix(in srgb, var(--brand-primary) 88%, black);
  min-height: calc(100vh - var(--topbar-h));
}

/* =========================================================================
   Embed mode
   ========================================================================= */

/* Keep the topbar visible in embed mode but slim it down to just the
   space-switcher tabs (Docs / API Reference / Changelog). The agency app
   already supplies its own brand chrome around the iframe, so the tabs are
   the only piece that genuinely belongs inside the embed. Shrinking
   --topbar-h keeps sidebar sticky offsets and scroll-margin-top in sync. */
.embed {
  --topbar-h: 48px;
}
.embed .brand-logo {
  /* 48px topbar in the embed: keep the scaled logo inside it. */
  max-height: 32px;
}
.embed .topbar {
  padding: 0 16px;
  background: var(--surface-1);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.embed .topbar .brand {
  display: none;
}
/* Keep the search trigger visible inside the embed — agencies asked for it
   even though the iframe sits inside their app chrome. The "Open <app>"
   link is still hidden because it would punt users out of the embed. */
.embed .topbar .topbar-right {
  display: flex;
}
.embed .topbar .topbar-link {
  display: none;
}
.embed .topbar .search-trigger {
  height: 32px;
  font-size: 13px;
}
/* Keep the placeholder label visible — hiding it left a full-width pill that
   read as an empty input with no placeholder. Only the ⌘K hint is dropped:
   inside an iframe the shortcut needs iframe focus, so advertising it is
   more misleading than helpful. */
.embed .topbar .search-trigger-kbd {
  display: none;
}
.embed .topnav {
  margin-left: 0;
}

.embed body::before {
  display: none;
}

.embed .shell {
  grid-template-columns: 240px 1fr;
}
/* OpenAPI pages don't render a sidebar — restore the single-column track
   the base `.shell-openapi` rule sets, otherwise the lone `<main>` lands
   in the 240px-wide sidebar slot and the spec wraps to ~10px wide. */
.embed .shell-openapi {
  grid-template-columns: 1fr;
}

/* Embed keeps the default two-column content grid, so the "On this page"
   TOC stays available inside the app (it collapses below 1100px via the
   base responsive rule, which tracks the IFRAME's width). Only the padding
   is tightened. */
.embed .content {
  padding: 28px 36px 56px;
}
.embed .content-openapi {
  padding: 0;
}

/* =========================================================================
   Focus mode (?focus=1, implies embed)
   The page is rendered inside the app's per-page help modal, so ALL site
   chrome goes: no topbar, no space tabs, no sidebar — just the article.
   Internal links keep the query string (propagated at runtime), so a reader
   who follows a cross-reference stays in focus mode.
   ========================================================================= */

.focus {
  --topbar-h: 0px;
}
.focus .topbar,
.focus .topnav,
.focus .sidebar,
.focus .sidebar-backdrop {
  display: none;
}
.focus .shell {
  grid-template-columns: 1fr;
}
.focus .content {
  padding: 8px 28px 40px;
}
/* Center the article column — with the sidebar gone the page would
   otherwise hug the left edge and leave a dead gutter on the right.
   `width: 100%` is load-bearing: auto margins on a grid item switch it from
   `stretch` to fit-content sizing, so without it the article grew to the
   walkthrough controls' max-content width on phones and ran past the
   right padding inside the app's help modal (2026-08-28). */
.focus .page {
  width: 100%;
  margin-inline: auto;
}

/* =========================================================================
   Page-actions toolbar (top-right of every content page)
   ========================================================================= */

/* The toolbar is rendered as the first child inside <article class="page">
   so the article keeps its grid cell. -8px margin-bottom lets the H1 sit
   close beneath the toolbar instead of dragging an empty band along. */
.page-toolbar {
  display: flex;
  justify-content: flex-end;
  margin: -8px 0 -8px;
}

.page-actions {
  position: relative;
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border-default);
  border-radius: var(--r-sm);
  background: var(--surface-0);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-body);
  box-shadow: var(--shadow-xs);
}

.page-actions-copy,
.page-actions-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.page-actions-copy {
  padding-right: 8px;
}
.page-actions-toggle {
  padding: 6px 8px;
  border-left: 1px solid var(--border-default);
}
.page-actions-toggle svg {
  transform: rotate(180deg);
  transition: transform 0.15s var(--ease);
}
.page-actions-toggle[aria-expanded="true"] svg {
  transform: rotate(0);
}
.page-actions-copy svg,
.page-actions-toggle svg {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  color: var(--text-muted);
}

.page-actions-copy:hover,
.page-actions-toggle:hover {
  background: var(--surface-1);
}

.page-actions-copy.is-flash {
  color: var(--brand-primary);
}

.page-actions-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 280px;
  padding: 6px;
  background: var(--surface-0);
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  z-index: 20;
}

.page-actions-menu > * {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  color: var(--text-body);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.page-actions-menu > *:hover {
  background: var(--surface-1);
  color: var(--text-strong);
}
.page-actions-menu svg {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  margin-top: 2px;
  color: var(--text-muted);
}
.page-actions-menu strong {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 600;
  color: var(--text-strong);
}
.page-actions-menu strong svg {
  width: 11px;
  height: 11px;
  flex: 0 0 11px;
  margin: 0;
  color: currentColor;
}
.page-actions-menu em {
  display: block;
  font-style: normal;
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
}
.page-actions-sep {
  height: 1px;
  background: var(--border-subtle);
  margin: 4px 4px;
}

/* =========================================================================
   Code block copy buttons
   ========================================================================= */

.code-block {
  position: relative;
}
.code-block pre {
  margin: 0;
}
.page .code-block {
  margin: 1.25em 0;
}

.code-copy {
  position: absolute;
  top: 8px;
  right: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: var(--surface-0);
  border: 1px solid var(--border-default);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity 0.12s var(--ease),
    color 0.12s var(--ease),
    background 0.12s var(--ease);
}
.code-block:hover .code-copy,
.code-copy:focus-visible {
  opacity: 1;
}
.code-copy:hover {
  color: var(--text-strong);
  background: var(--surface-1);
}
.code-copy svg {
  width: 14px;
  height: 14px;
}
.code-copy.is-copied {
  color: var(--brand-primary);
  border-color: color-mix(
    in srgb,
    var(--brand-primary) 50%,
    var(--border-default)
  );
}
.code-copy.is-copied::after {
  content: "Copied";
  position: absolute;
  right: calc(100% + 6px);
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--brand-primary);
  white-space: nowrap;
}

/* =========================================================================
   Responsive
   ========================================================================= */

@media (max-width: 1100px) {
  .content {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
  .toc {
    display: none;
  }
}

@media (max-width: 820px) {
  :root {
    --topbar-h: 56px;
  }
  .shell {
    grid-template-columns: 1fr;
  }
  .sidebar-mobile-only {
    display: block;
  }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: min(320px, 85vw);
    height: 100vh;
    max-height: 100vh;
    padding: 20px 20px 24px;
    background: var(--surface-0);
    border-right: 1px solid var(--border-default);
    border-bottom: none;
    box-shadow: 4px 0 24px rgba(15, 23, 42, 0.08);
    transform: translateX(-100%);
    transition: transform 0.22s var(--ease);
    z-index: 60;
    overscroll-behavior: contain;
  }
  .sidebar.is-open {
    transform: translateX(0);
  }
  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.4);
    z-index: 55;
    opacity: 0;
    transition: opacity 0.22s var(--ease);
  }
  .sidebar-backdrop.is-visible {
    opacity: 1;
  }
  body.sidebar-open {
    overflow: hidden;
  }
  .sidebar-toggle {
    display: inline-flex;
  }
  .sidebar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: -4px 0 16px;
  }
  .sidebar-brand {
    flex: 1;
    min-width: 0;
  }
  .sidebar-close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--border-default);
    border-radius: var(--r-sm);
    color: var(--text-strong);
    cursor: pointer;
  }
  .sidebar-close:hover {
    background: var(--surface-2);
  }
  .sidebar-close svg {
    width: 18px;
    height: 18px;
  }
  .sidebar-spaces {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-bottom: 16px;
    margin-bottom: 16px;
    border-bottom: 1px solid var(--border-subtle);
  }
  .sidebar-space-link {
    display: block;
    padding: 8px 10px;
    border-radius: var(--r-sm);
    color: var(--text-muted);
    font-size: 14px;
    font-weight: 500;
    transition:
      background 0.12s var(--ease),
      color 0.12s var(--ease);
  }
  .sidebar-space-link:hover {
    background: var(--surface-2);
    color: var(--text-strong);
    text-decoration: none;
  }
  .sidebar-space-link.is-active {
    color: var(--brand-primary);
    background: color-mix(in srgb, var(--brand-primary) 10%, transparent);
  }
  .content {
    padding: 28px 20px 64px;
  }
  .page h1 {
    font-size: 2rem;
  }
  .topbar {
    padding: 0 16px;
    gap: 12px;
  }
  /* Space links (Docs / API Ref / What's New) move into the drawer on
     mobile — hide the desktop topbar copy. */
  .topnav {
    display: none;
  }
  /* "Open app" link is redundant on mobile — the hamburger + topnav already
     own the available space. Hide it to keep the bar uncluttered. */
  .topbar-link {
    display: none;
  }
  /* Compact icon-only search button on mobile — matches the hamburger's
     square footprint instead of the stretched desktop input look. */
  .search-trigger {
    width: 36px;
    height: 36px;
    padding: 0;
    flex: 0 0 auto;
    justify-content: center;
    gap: 0;
  }
  .search-trigger-icon {
    width: 18px;
    height: 18px;
  }
  .search-trigger-label,
  .search-trigger-kbd {
    display: none;
  }
}

/* =========================================================================
   Language switcher (topbar). Native <details> dropdown — toggles without JS.
   Hidden inside the embed: the Help widget picks the language via the path,
   so the docs language always tracks the app's UI language there.
   ========================================================================= */

.lang-switcher {
  position: relative;
}
.lang-switcher .lang-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-default);
  background: var(--surface-0);
  color: var(--text-body);
  font-size: 14px;
  font-weight: 500;
  list-style: none;
  cursor: pointer;
  user-select: none;
  transition:
    background 0.12s var(--ease),
    border-color 0.12s var(--ease);
}
.lang-switcher .lang-trigger::-webkit-details-marker {
  display: none;
}
.lang-switcher .lang-trigger:hover {
  background: var(--surface-1);
  border-color: var(--border-strong);
}
.lang-switcher .lang-globe {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  color: var(--text-muted);
}
.lang-switcher .lang-chevron {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  color: var(--text-muted);
  transition: transform 0.15s var(--ease);
}
.lang-switcher[open] .lang-chevron {
  transform: rotate(180deg);
}
.lang-switcher .lang-menu {
  position: absolute;
  top: calc(100% + 6px);
  inset-inline-end: 0;
  min-width: 200px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 6px;
  background: var(--surface-0);
  border: 1px solid var(--border-default);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  z-index: 30;
}
.lang-switcher .lang-option {
  display: block;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  color: var(--text-body);
  font-size: 14px;
  text-decoration: none;
  white-space: nowrap;
}
.lang-switcher .lang-option:hover {
  background: var(--surface-1);
  color: var(--text-strong);
  text-decoration: none;
}
.lang-switcher .lang-option.is-active {
  color: var(--brand-primary);
  font-weight: 600;
}
.embed .lang-switcher {
  display: none;
}
@media (max-width: 820px) {
  /* The native current-language label is enough on mobile; drop it to keep
     the bar tight, leaving the globe as the affordance. */
  .lang-switcher .lang-current {
    display: none;
  }
}
