/*
 * Chatleadr documentation.
 *
 * EVERY COLOUR IN THIS FILE IS A TOKEN FROM Dashboard/tailwind.config.js. Not a
 * near neighbour of one, not a Tailwind default that happens to look similar --
 * the same hex, copied across. The ramps are declared once at the top of :root
 * and nothing below them is allowed to write a literal colour, so the docs and
 * the product cannot drift apart one afternoon at a time.
 *
 * `tests/unit/docs-brand.test.js` reads tailwind.config.js and fails on any hex
 * in this file that is not in it. That check is the only thing that makes the
 * paragraph above true a year from now, and it caught the two that had already
 * slipped: the danger callout was painted in Tailwind's red-50 and red-200
 * rather than the brand's danger-50 and danger-200, which are warmer, and the
 * figure shadow was mixed from a blue-black that is in no palette we own.
 *
 * Three colour families, each with one job:
 *
 *   ink      flat greys, near-zero chroma. Everything structural.
 *   brand    the purple ramp. Every interactive thing, and nothing else.
 *   accent   the lime. A mark on dark grounds and the resting state of a
 *            primary button -- #d9e94a on white is roughly 1.6:1, so it can
 *            never carry text there.
 *
 * PURPLE IS THE HOVER STATE. Every button in the product moves to
 * primary-purple when pointed at -- see BaseButton.vue, where both the primary
 * and secondary variants do exactly that -- so every button here does too. A
 * lime button that brightens to a paler lime is the tell that a page was
 * styled from a screenshot rather than from the brand.
 *
 * LIGHT ONLY, DELIBERATELY. There is no dark theme, no toggle and no
 * `prefers-color-scheme` block. The product has one appearance; a documentation
 * site with two has to prove every figure, callout and code sample in both, and
 * the screenshots -- which are reconstructions of an application that is light
 * in every case -- would sit in a dark page looking like a different product.
 * `color-scheme: light` below tells the browser the same thing, so form
 * controls, scrollbars and the like are not re-coloured underneath us.
 *
 * CASING IS SET IN THE MARKUP, NOT HERE. There is no text-transform anywhere in
 * this file, on purpose: headings and buttons are written in Title Case and
 * everything else in sentence case, and a stylesheet that shouts a label the
 * author wrote quietly puts those two rules permanently out of reach.
 */

@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }
}

:root {
  /* ---------------------------------------------------------------- tokens */
  /* Verbatim from Dashboard/tailwind.config.js. Nothing else in this file is
     allowed to write a hex. Only the stops the docs actually use are declared,
     so an unused ramp cannot rot unnoticed. */

  --ink-50:  #fafafa;
  --ink-100: #f4f4f5;
  --ink-200: #e7e7e8;
  --ink-300: #d4d4d6;
  --ink-400: #a6a6a9;
  --ink-500: #78787b;   /* 4.8:1 on white -- the lightest stop safe for body text */
  --ink-600: #5b5b5e;
  --ink-800: #2e2e30;
  --ink-900: #1c1c1e;

  --brand-50:  #f6f1ff;
  --brand-100: #ebe0ff;
  --brand-200: #dbbfff;  /* = primary.purple */
  --brand-600: #7042cc;  /* 6.1:1 on white -- the interactive stop */
  --brand-700: #5a34ae;
  --brand-800: #44379d;  /* = primary.blue */

  --warning-50:  #fdf8ed;
  --warning-200: #f2d89b;
  --danger-50:   #fdf4f3;
  --danger-200:  #fbd0cd;

  --primary:      #1e2430;
  --primary-dark: #131726;
  --accent:       #d9e94a;
  --white:        #ffffff;

  /* ------------------------------------------------------------- semantics */
  /* What each token is for on this site. Component rules below reference only
     these names, so re-pointing a role is one line rather than a search. */

  --ground: var(--white);
  --surface: var(--ink-50);
  --surface-2: var(--ink-100);
  --rule: var(--ink-200);
  --rule-strong: var(--ink-300);

  --text: var(--ink-800);
  --text-strong: var(--ink-900);
  --text-muted: var(--ink-600);
  --text-faint: var(--ink-500);

  --link: var(--brand-600);
  --link-hover: var(--brand-700);

  --code-bg: var(--ink-100);
  --code-text: var(--brand-800);

  --topbar: var(--primary);
  --topbar-text: var(--ink-100);
  --topbar-muted: var(--ink-400);
  /* A hairline on the dark bar, mixed from the bar's own text rather than from
     a bare white at some opacity -- which is how a value nobody can trace gets
     into a stylesheet. */
  --topbar-rule: color-mix(in srgb, var(--topbar-text) 12%, transparent);
  --topbar-rule-strong: color-mix(in srgb, var(--topbar-text) 22%, transparent);

  --hover: var(--brand-200);   /* primary-purple: what a button becomes on hover */
  --on-hover: var(--ink-900);

  --note: var(--brand-50);
  --note-rule: var(--brand-200);
  --warn: var(--warning-50);
  --warn-rule: var(--warning-200);
  --danger: var(--danger-50);
  --danger-rule: var(--danger-200);

  /* The shadow ground. primary-dark is the darkest thing the brand owns, and a
     shadow mixed from it sits under the page instead of next to it. */
  --shadow-near: color-mix(in srgb, var(--primary-dark) 8%, transparent);
  --shadow-far: color-mix(in srgb, var(--primary-dark) 26%, transparent);

  --radius: 10px;
  --sidebar-w: 17rem;
  --toc-w: 14rem;
  --topbar-h: 3.5rem;
  --measure: 68ch;

  /*
   * THE PRODUCT'S THREE FACES, AND THE ONE RULE THAT DECIDES BETWEEN THEM:
   *
   *   --title   Frank Ruhl Libre. Headings and the wordmark. Nothing else.
   *   --sans    Mulish. Everything the reader READS: paragraphs, lists, tables,
   *             callouts, figure captions.
   *   --ui      Inter. Everything the reader OPERATES: topbar, sidebar, contents,
   *             search, buttons, pager, footer, the language tag on a code block.
   *
   * The rule exists because the site had drifted off it in three places, and the
   * drift is visible without knowing any of this: an h4 was set in Inter under
   * an h3 set in Frank Ruhl Libre, and every table and figure caption was set in
   * Inter in the middle of Mulish paragraphs. Two faces inside one column reads
   * as a page assembled from two designs.
   */
  --title: "Frank Ruhl Libre", Georgia, "Times New Roman", serif;
  --sans: Mulish, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --ui: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /*
   * ONE TYPE SCALE. Seventeen distinct font sizes were in this file, including
   * 0.78 / 0.80 / 0.82rem and 0.85 / 0.855 / 0.88rem -- differences nobody chose
   * and nobody can see, which is what makes a page look unresolved rather than
   * quiet. Every size below is one of these eight, and a new component picks the
   * nearest rather than inventing another.
   */
  --fs-tiny:  0.72rem;   /* keyboard hint, code language tag */
  --fs-micro: 0.8rem;    /* nav and contents labels, pager eyebrow, table head */
  --fs-small: 0.875rem;  /* sidebar, tables, captions, footer, buttons */
  --fs-body:  1rem;
  --fs-lead:  1.125rem;  /* the standfirst under a page title */
  --fs-h3:    1.25rem;
  --fs-h2:    1.5rem;
  --fs-h1:    clamp(2rem, 1.4rem + 2vw, 2.6rem);

  color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: calc(var(--topbar-h) + 1rem); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--link); text-decoration-color: color-mix(in srgb, var(--link) 35%, transparent); text-underline-offset: 2px; }
a:hover { color: var(--link-hover); text-decoration-color: currentColor; }
:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; border-radius: 3px; }

svg { display: block; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ground); padding: 0.75rem 1rem; border: 1px solid var(--rule);
}
.skip:focus { left: 0.5rem; top: 0.5rem; }

/* ------------------------------------------------------------------ topbar */

.topbar {
  position: sticky; top: 0; z-index: 40;
  height: var(--topbar-h);
  background: var(--topbar);
  color: var(--topbar-text);
  border-bottom: 1px solid var(--topbar-rule);
}

.topbar-inner {
  height: 100%; margin: 0 auto; max-width: 90rem;
  padding: 0 1.25rem;
  display: flex; align-items: center; gap: 1.5rem;
}

/* The wordmark, as the product draws it: lowercase, Frank Ruhl Libre, lime.
   The face has a modest x-height, so it needs a size above its neighbours to
   read as the same weight. */
.brand { display: flex; align-items: baseline; gap: 0.45rem; color: inherit; text-decoration: none; }
.brand:hover { color: inherit; }
.brand-name {
  font-family: var(--title); font-weight: 700; font-size: 1.6rem; line-height: 1;
  color: var(--accent); letter-spacing: -0.01em;
}
.brand-sub { font-family: var(--ui); font-weight: 600; font-size: var(--fs-micro); color: var(--topbar-muted); }
.brand:hover .brand-sub { color: var(--hover); }

.areas { display: flex; gap: 1.25rem; font-family: var(--ui); font-size: var(--fs-small); }
.areas a { color: var(--topbar-muted); text-decoration: none; padding: 0.25rem 0; border-bottom: 2px solid transparent; }
.areas a:hover { color: var(--hover); }
.areas a.current { color: var(--topbar-text); border-bottom-color: var(--accent); }

/* `margin-left: auto` here rather than `margin-right: auto` on `.areas`, which
   is what used to do it. Below 900px the area links are display:none, and the
   auto margin went with them -- so search, the button and the menu icon
   collapsed against the wordmark instead of sitting at the right edge. The
   alignment now belongs to the thing being aligned. */
.topbar-actions { display: flex; align-items: center; gap: 0.6rem; margin-left: auto; }

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

.search { position: relative; }
.search-field {
  display: flex; align-items: center; gap: 0.45rem;
  background: color-mix(in srgb, var(--topbar-text) 8%, transparent);
  border: 1px solid var(--topbar-rule-strong);
  border-radius: 8px; padding: 0 0.55rem; height: 2rem;
  transition: background-color 0.15s, border-color 0.15s;
}
.search-field:focus-within { background: var(--ground); border-color: var(--accent); }
.search-icon { display: grid; place-items: center; color: var(--topbar-muted); }
.search-field:focus-within .search-icon { color: var(--text-faint); }

.search input {
  background: none; border: 0; outline: none; padding: 0;
  font-family: var(--ui); font-size: var(--fs-small); color: var(--topbar-text);
  width: 9rem;
}
.search input::placeholder { color: var(--topbar-muted); }
.search-field:focus-within input { color: var(--text); width: 14rem; }
.search input::-webkit-search-cancel-button { display: none; }

.search kbd {
  font-family: var(--ui); font-size: var(--fs-tiny); font-weight: 600;
  color: var(--topbar-muted); border: 1px solid var(--topbar-rule-strong);
  border-radius: 4px; padding: 0 0.28rem; line-height: 1.35;
}
.search-field:focus-within kbd { display: none; }

/* Results hang off the field rather than taking layout, so opening them never
   moves the page under the cursor. */
.search-results { display: none; }
.search.open .search-results {
  display: block;
  position: absolute; top: calc(100% + 0.4rem); right: 0;
  width: min(26rem, calc(100vw - 2rem));
  background: var(--ground); color: var(--text);
  border: 1px solid var(--rule); border-radius: var(--radius);
  box-shadow: 0 1px 2px var(--shadow-near), 0 12px 32px -12px var(--shadow-far);
  max-height: min(28rem, calc(100vh - 5rem)); overflow-y: auto;
  padding: 0.35rem; z-index: 50;
}
.search-results a {
  display: block; padding: 0.5rem 0.6rem; border-radius: 7px;
  text-decoration: none; color: var(--text-strong);
  font-family: var(--ui); font-size: var(--fs-small); font-weight: 600;
}
.search-results a.current { background: var(--surface-2); }
.search-results a:hover { background: var(--surface-2); }
.search-area {
  display: block; font-size: var(--fs-tiny); font-weight: 700;
  color: var(--link); margin-bottom: 0.1rem;
}
/* -webkit-box, not block: the second `display` was overriding the first, which
   is the only reason the two-line clamp worked at all. Declared once. */
.search-desc {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-weight: 400; font-size: var(--fs-micro); line-height: 1.45;
  color: var(--text-faint); margin-top: 0.15rem;
}
.search-empty {
  margin: 0; padding: 0.7rem; font-family: var(--ui);
  font-size: var(--fs-small); color: var(--text-faint);
}

.icon-btn {
  display: grid; place-items: center;
  background: transparent; border: 1px solid var(--topbar-rule-strong);
  color: var(--topbar-text); border-radius: 8px;
  width: 2rem; height: 2rem; cursor: pointer; padding: 0;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}
.icon-btn:hover { background: var(--hover); color: var(--on-hover); border-color: var(--hover); }
.nav-btn { display: none; }

/* The primary button, matching BaseButton: lime at rest, purple on hover, ink
   text throughout so the label never has to change colour with the ground. */
.cta {
  background: var(--accent); color: var(--on-hover); text-decoration: none;
  font-family: var(--ui); font-weight: 700; font-size: var(--fs-small);
  padding: 0.42rem 0.9rem; border-radius: 8px; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  transition: background-color 0.2s, border-color 0.2s;
}
.cta:hover { background: var(--hover); border-color: var(--hover); color: var(--on-hover); }
.cta:active { transform: scale(0.97); }

/* ------------------------------------------------------------------- shell */

.shell {
  margin: 0 auto; max-width: 90rem; padding: 0 1.25rem;
  display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  gap: 2.5rem; align-items: start;
}

.sidebar {
  position: sticky; top: var(--topbar-h);
  max-height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  padding: 2rem 0.5rem 3rem 0;
  font-family: var(--ui); font-size: var(--fs-small);
}

.nav-section + .nav-section { margin-top: 0.35rem; }

/* A SECTION HEADING OUTRANKS THE PAGES UNDER IT.
   It was --text-faint at micro size: lighter than the page links beneath it,
   so the column read as one long list with some odd pale entries in it rather
   than as ten labelled groups. It is the darkest text in the sidebar now, and
   the only bold thing in it. */
.nav-title {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  margin: 0; padding: 0.4rem 0.7rem 0.4rem 0; 
  font-size: var(--fs-small); font-weight: 700;
  letter-spacing: 0.01em; color: var(--text-strong);
  cursor: pointer; list-style: none; border-radius: 6px;
  user-select: none;
}
/* Safari draws its own triangle without this, beside ours. */
.nav-title::-webkit-details-marker { display: none; }
.nav-title:hover { color: var(--link); }
.nav-title svg { flex: none; color: var(--text-faint); transition: transform 0.18s ease; }
.nav-section[open] > .nav-title svg { transform: rotate(180deg); }
.nav-section > ul { padding-bottom: 0.5rem; }

/* THE SECTION OPENS RATHER THAN APPEARING.
 *
 * A <details> toggles display, so the usual height transition has nothing to
 * animate between: the content is not in the layout one frame and fully laid
 * out the next. ::details-content is the box the browser puts that content in,
 * and with interpolate-size it can be transitioned from 0 to auto, which is the
 * one thing height animation has always needed and never had.
 *
 * content-visibility rides along with allow-discrete so the content is not
 * hidden until the size transition has finished, otherwise the closing frame
 * pops rather than collapses.
 *
 * NO JAVASCRIPT, and no fallback needed. A browser without ::details-content
 * ignores all of this and the section snaps open, which is what it did before.
 */
.nav-section::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.22s ease, content-visibility 0.22s allow-discrete;
}
.nav-section[open]::details-content { block-size: auto; }
.nav-title:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }

.sidebar ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.sidebar li a {
  display: block; padding: 0.28rem 0.7rem; margin-left: -0.7rem;
  color: var(--text-muted); text-decoration: none; border-radius: 6px;
  border-left: 2px solid transparent;
}
.sidebar li a:hover { color: var(--link); background: var(--surface-2); }
.sidebar li a.current {
  color: var(--link); font-weight: 600;
  border-left-color: var(--link); background: var(--surface-2);
}

/* WHERE YOU ARE, AND THE OTHER TWO PLACES YOU COULD BE.
   Shown only where the topbar's own area links are hidden, so a phone is never
   in one area with no way out of it. It lists all three including the current
   one: a switcher that omits the option you are using does not tell you which
   one that is. Sits at the TOP of the drawer, because choosing between
   Documentation, Guides and Compare comes before choosing a page inside one. */
.nav-switch { display: none; }
.nav-switch ul { gap: 0.25rem; }
.nav-switch li a {
  display: block; padding: 0.5rem 0.7rem; border-radius: 8px;
  border: 1px solid var(--rule); border-left-width: 1px;
  color: var(--text-strong); font-weight: 600;
}
/* THE SELECTED AREA IS THE BRAND'S LIME, not the link purple. Purple is what
   a link is in this stylesheet, so a filled purple row read as a very large
   link rather than as the row you are on. Lime is 1.6:1 against white and
   cannot carry white text, which is why the label goes to the darkest ink
   rather than staying in the link colour. */
.nav-switch li a.current {
  border-color: var(--accent); background: var(--accent); color: var(--text-strong);
}

/* The dashboard link, for the widths where the topbar cannot carry it. */
.nav-foot { display: none; margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); }
.nav-cta {
  display: block; text-align: center; padding: 0.6rem 1rem; border-radius: 8px;
  background: var(--accent); color: var(--ink-900);
  font-family: var(--ui); font-weight: 700; font-size: var(--fs-small);
  text-decoration: none; border: 1px solid var(--accent);
}
.nav-cta:hover { background: var(--hover); border-color: var(--hover); color: var(--on-hover); }

main {
  display: grid; grid-template-columns: minmax(0, 1fr) var(--toc-w);
  gap: 3rem; padding: 2rem 0 5rem; align-items: start;
}

/* -------------------------------------------------------------------- prose */

.prose { min-width: 0; max-width: var(--measure); }

.page-head { margin-bottom: 2rem; }

/* All four heading levels in the same face. h4 was the exception -- Inter, and
   smaller than the paragraph under it -- so the deepest heading on a page read
   as a bold label rather than as the bottom of the hierarchy. */
.prose h1, .prose h2, .prose h3, .prose h4 {
  font-family: var(--title); font-weight: 700; color: var(--text-strong);
  text-wrap: balance;
}

.prose h1 {
  margin: 0 0 0.6rem; font-size: var(--fs-h1);
  line-height: 1.15; letter-spacing: -0.015em;
}

.prose h2 {
  margin: 3rem 0 1rem; padding-top: 1.5rem; border-top: 1px solid var(--rule);
  font-size: var(--fs-h2); letter-spacing: -0.01em;
}
.prose h3 { margin: 2.2rem 0 0.6rem; font-size: var(--fs-h3); }
.prose h4 { margin: 1.8rem 0 0.5rem; font-size: var(--fs-body); }

.prose p { margin: 0 0 1.1rem; text-wrap: pretty; }
.prose ul, .prose ol { margin: 0 0 1.2rem; padding-left: 1.3rem; display: flex; flex-direction: column; gap: 0.4rem; }
.prose li { padding-left: 0.2rem; }
.prose li::marker { color: var(--text-faint); }

.prose strong { color: var(--text-strong); font-weight: 700; }
.prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }

/*
 * THE ANCHOR IS A FEATURE, NOT A HOVER GARNISH.
 *
 * Every h2, h3 and h4 carries an id and a link to it, because the way one
 * person sends another person a piece of documentation is by sending the
 * paragraph, not the page. Three things were wrong with revealing it only on
 * `h2:hover, h3:hover`:
 *
 *   h4 was excluded, so its anchor existed in the markup at opacity 0 with no
 *   selector able to bring it back -- a link that is present, focusable and
 *   permanently invisible.
 *
 *   A touch device has no hover, so on a phone NO anchor was ever visible.
 *   That is most of the traffic, and it is exactly the reader most likely to
 *   want to send a link rather than read on.
 *
 *   Focus revealed it, but tabbing to a heading link nobody can see first is
 *   not a way anyone finds a feature.
 *
 * So: faint but present at rest, solid on hover or focus, and reserved space so
 * nothing reflows. `scroll-padding-top` on <html> is what keeps the landing
 * heading clear of the sticky topbar once the link is followed.
 */
.anchor {
  margin-left: 0.4rem; color: var(--text-faint); text-decoration: none;
  opacity: 0.28; font-weight: 400;
  transition: opacity 0.12s ease, color 0.12s ease;
}
.prose h2:hover .anchor,
.prose h3:hover .anchor,
.prose h4:hover .anchor,
.anchor:hover, .anchor:focus-visible { opacity: 1; color: var(--link); }

/* A pointer that can hover gets the quieter resting state; a touch screen keeps
   the anchor legible, because there is no gesture there that would reveal it. */
@media (hover: hover) and (pointer: fine) {
  .anchor { opacity: 0; }
  .prose h2:hover .anchor,
  .prose h3:hover .anchor,
  .prose h4:hover .anchor,
  .anchor:hover, .anchor:focus-visible { opacity: 1; }
}

/* --------------------------------------------------------------------- code */

code {
  font-family: var(--mono); font-size: 0.875em;
  background: var(--code-bg); color: var(--code-text);
  padding: 0.12em 0.36em; border-radius: 4px;
  overflow-wrap: break-word;
}

.code-block {
  position: relative; margin: 0 0 1.4rem;
  background: var(--surface-2); border: 1px solid var(--rule);
  border-radius: var(--radius); overflow: hidden;
}
.code-lang {
  position: absolute; top: 0; right: 0;
  font-family: var(--ui); font-size: var(--fs-tiny); font-weight: 600;
  color: var(--text-faint); padding: 0.4rem 0.7rem;
}
.code-block pre { margin: 0; padding: 1rem 1.1rem; overflow-x: auto; }
.code-block code {
  background: none; color: var(--text); padding: 0;
  font-size: var(--fs-small); line-height: 1.65; white-space: pre;
}

/* ------------------------------------------------------------------- tables */

/* A table is content, so it is set in the body face. It was in Inter, which put
   two typefaces in one reading column every time a paragraph introduced a table.
   Tabular figures stay: a price column that does not line up is a price column
   the reader has to compare digit by digit. */
.table-wrap { overflow-x: auto; margin: 0 0 1.5rem; border: 1px solid var(--rule); border-radius: var(--radius); }
table { border-collapse: collapse; width: 100%; font-family: var(--sans); font-size: var(--fs-small); font-variant-numeric: tabular-nums; }
th, td { text-align: left; padding: 0.6rem 0.9rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { background: var(--surface-2); font-size: var(--fs-micro); font-weight: 700; color: var(--text-strong); white-space: nowrap; }
tbody tr:last-child td { border-bottom: 0; }

/* ----------------------------------------------------------------- callouts */

.callout {
  margin: 0 0 1.4rem; padding: 1rem 1.1rem;
  border: 1px solid var(--rule); border-left-width: 3px;
  border-radius: var(--radius); background: var(--surface);
}
.callout > :last-child { margin-bottom: 0; }
.callout-title { font-weight: 700; color: var(--text-strong); margin: 0 0 0.4rem; }
.callout-note, .callout-tip { background: var(--note); border-left-color: var(--note-rule); }
.callout-warning { background: var(--warn); border-left-color: var(--warn-rule); }
.callout-danger { background: var(--danger); border-left-color: var(--danger-rule); }

blockquote {
  margin: 0 0 1.4rem; padding-left: 1.1rem;
  border-left: 3px solid var(--rule-strong); color: var(--text-muted);
}
blockquote > :last-child { margin-bottom: 0; }

/* ------------------------------------------------------------------ figures */

/*
 * Two kinds, styled apart on purpose.
 *
 * A screenshot is a picture of the application, painted in the application's
 * own colours and framed so it reads as one. A diagram is ours, drawn against
 * the same custom properties as the text, so re-pointing a token above moves
 * the drawings with the prose instead of leaving them behind.
 */
.figure { margin: 0 0 1.8rem; }
.figure-body { border-radius: var(--radius); overflow: hidden; }
.figure svg { width: 100%; height: auto; }

.figure-shot .figure-body {
  border: 1px solid var(--rule-strong);
  background: var(--ground);
  box-shadow: 0 1px 2px var(--shadow-near), 0 8px 24px -12px var(--shadow-far);
}

.figure-diagram .figure-body { padding: 0.25rem 0; }

/* A caption is a sentence about the picture, so it is body copy at a smaller
   size, not interface furniture -- and at ink-600 rather than ink-500, because
   it is meant to be read rather than glanced past. */
.figure figcaption {
  margin-top: 0.7rem; font-size: var(--fs-small); line-height: 1.5;
  color: var(--text-muted); text-wrap: pretty;
}

/* A figure may break the reading measure, because a workflow drawn at 68
   characters wide is a workflow nobody can read. It stops at the column, never
   at the viewport. */
.figure-wide { width: min(100%, 54rem); }
@media (min-width: 1181px) {
  .figure-wide { width: calc(100% + 8rem); max-width: min(54rem, calc(100vw - 24rem)); }
}

/* A row of node cards, each with its own caption, laid out as a grid rather
   than as one wide drawing. Below the breakpoint they stack, which is the
   right reading order anyway. */
.figure-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem 2rem; margin: 0 0 1.8rem; }
.figure-grid .figure { margin: 0; }
@media (max-width: 900px) { .figure-grid { grid-template-columns: minmax(0, 1fr); } }

/* ---------------------------------------------------------------------- toc */

.toc { position: sticky; top: calc(var(--topbar-h) + 2rem); font-family: var(--ui); font-size: var(--fs-micro); }
.toc-title {
  margin: 0 0 0.6rem; font-size: var(--fs-micro); font-weight: 700;
  color: var(--text-faint);
}
.toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem;
  border-left: 1px solid var(--rule); }
.toc a { display: block; padding: 0.2rem 0 0.2rem 0.8rem; margin-left: -1px;
  color: var(--text-muted); text-decoration: none; border-left: 1px solid transparent; }
.toc a:hover { color: var(--link); }
.toc a.current { color: var(--link); border-left-color: var(--link); }
.toc-h3 a { padding-left: 1.6rem; font-size: 0.95em; }

/* -------------------------------------------------------------------- pager */

/* Navigation, so the whole card is set in the interface face. The eyebrow was
   already Inter and the page name under it was Mulish, which is two typefaces
   inside one 40mm-wide control. */
.pager { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-top: 4rem; }
.pager a {
  display: flex; flex-direction: column; gap: 0.15rem;
  padding: 0.9rem 1.1rem; border: 1px solid var(--rule); border-radius: var(--radius);
  text-decoration: none; color: var(--text-strong);
  font-family: var(--ui); font-size: var(--fs-small); font-weight: 600;
}
.pager a:hover { border-color: var(--link); color: var(--link); }
.pager a span {
  display: flex; align-items: center; gap: 0.3rem;
  font-family: var(--ui); font-size: var(--fs-micro); font-weight: 600; color: var(--text-faint);
}
.pager-next { text-align: right; }
.pager-next span { justify-content: flex-end; }

/* ------------------------------------------------------------------- footer */

.foot { border-top: 1px solid var(--rule); background: var(--surface); }
.foot-inner {
  max-width: 90rem; margin: 0 auto; padding: 2rem 1.25rem;
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  font-family: var(--ui); font-size: var(--fs-small); color: var(--text-muted);
}
.foot p { margin: 0; }
.foot-links { display: flex; gap: 1.25rem; }
.foot-links a { text-decoration: none; }
.foot-links a:hover { text-decoration: underline; }

/* -------------------------------------------------------------- responsive */

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

/*
 * Below this width a figure scrolls inside its own frame rather than being
 * squeezed to the viewport, which is what a wide table already does here. The
 * page itself must never scroll sideways.
 *
 * THE FLOOR IS PER-FIGURE, because one fixed floor cannot serve both ends of
 * the range. It was a flat 32rem, and at that width on a 375px phone the
 * 1120-wide editor drawings rendered at 0.46 scale: an 11px label came out at
 * 5px, which is a drawing of a label rather than a label. Raising the flat
 * value far enough to fix that would have blown the 400-wide diagrams up well
 * past the size they were drawn at.
 *
 * `--fig-w` is the drawing's own width, set on the element by renderFigure, and
 * the floor is that width clamped between 32rem and 56rem. The old 32rem stays
 * the lower bound on purpose: a plain `min()` would have let the two narrowest
 * diagrams render SMALLER than they do today, which is a strange way to answer
 * "make them bigger". Nothing shrinks, and the widest drawings go from 0.46
 * scale to 0.80.
 */
@media (max-width: 760px) {
  .figure-body { overflow-x: auto; overscroll-behavior-x: contain; }
  .figure svg { min-width: clamp(32rem, calc(var(--fig-w, 512) * 1px), 56rem); }
  /* A single node card is drawn at 452 units and stays legible scaled down, so
     it is exempt -- it is one node, and making one node scroll is worse than
     reading it small. */
  .figure-card .figure-body { overflow-x: visible; }
  .figure-card svg { min-width: 0; }
}

/*
 * THE MOBILE NAVIGATION IS A DRAWER, NOT AN ACCORDION.
 *
 * It used to go from display:none to display:block inside the page flow, so
 * tapping the menu button pushed the article down by the height of the whole
 * navigation -- eleven sections on a documentation site -- and closing it again
 * meant scrolling back up to find the button. Nothing about that toggles
 * nicely: the content the reader was looking at leaves the screen.
 *
 * It slides over the page instead, under the topbar so the close button stays
 * put, with a backdrop that dismisses it. `visibility` rather than `display`
 * because it is animatable and still removes the panel from the tab order and
 * from a screen reader while it is shut.
 */
.scrim { display: none; }

/*
 * THE TWO MENU ICONS STACK IN ONE GRID CELL, not in two rows.
 *
 * `.icon-btn` is a 2rem grid, so two children were two rows: against a
 * stylesheet that had not yet learned to hide one of them, the hamburger and
 * the close cross rendered squashed one above the other inside a 32px button.
 * Sharing a cell means the worst a missing rule can do is draw them on top of
 * each other at the right size, and the fingerprinted stylesheet means a
 * missing rule cannot happen at all.
 */
.nav-btn .nav-icon { grid-area: 1 / 1; }
.nav-icon-close { visibility: hidden; }

@media (max-width: 900px) {
  .nav-btn { display: grid; }
  .areas { display: none; }
  /* The topbar's area links are gone at this width, so the drawer carries them
     instead. Without this the only route to Guides or Compare on a phone is the
     footer, past the whole article. */
  .nav-switch { display: block; margin-bottom: 1.5rem; }
  .nav-foot { display: block; }
  .shell { grid-template-columns: minmax(0, 1fr); gap: 0; }

  .sidebar {
    position: fixed; top: var(--topbar-h); left: 0; bottom: 0; z-index: 45;
    width: min(19rem, 84vw); max-height: none;
    overflow-y: auto; overscroll-behavior: contain;
    padding: 1.25rem 1.15rem 3rem;
    background: var(--ground); border-right: 1px solid var(--rule);
    box-shadow: 14px 0 34px -20px var(--shadow-far);
    transform: translateX(-100%); visibility: hidden;
    transition: transform 0.22s ease, visibility 0s linear 0.22s;
  }
  body.nav-open .sidebar {
    transform: none; visibility: visible;
    transition: transform 0.22s ease, visibility 0s;
  }

  .scrim {
    display: block; position: fixed; top: var(--topbar-h); right: 0; bottom: 0; left: 0;
    z-index: 44; border: 0; margin: 0; padding: 0; cursor: pointer;
    background: color-mix(in srgb, var(--primary-dark) 42%, transparent);
    opacity: 0; visibility: hidden;
    transition: opacity 0.22s ease, visibility 0s linear 0.22s;
  }
  body.nav-open .scrim {
    opacity: 1; visibility: visible;
    transition: opacity 0.22s ease, visibility 0s;
  }

  /* The page behind a drawer does not scroll. Without this a swipe inside the
     panel scrolls the article underneath once the list hits its end. */
  body.nav-open { overflow: hidden; }

  /* The button becomes the way out, so it shows the way out. */
  body.nav-open .nav-icon-open { visibility: hidden; }
  body.nav-open .nav-icon-close { visibility: visible; }

  .pager { grid-template-columns: 1fr; }
  .brand-name { font-size: 1.4rem; }
  .search input { width: 6rem; }
  .search-field:focus-within input { width: 9rem; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar, .scrim { transition: none; }
  .nav-section::details-content, .nav-title svg { transition: none; }
}

/* Narrower still, the CTA and the search field cannot both sit in the bar. The
   search stays: a reader who cannot find a page has no use for a button. */
@media (max-width: 620px) {
  .cta { display: none; }
  .brand-sub { display: none; }
  .search-field:focus-within input { width: 11rem; }
}
