/* Tokens live in tokens.css, generated from @ramonda/theme. */
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  background: var(--rmd-bg);
  color: var(--rmd-fg);
  font-size: 16px;
  line-height: 1.65;
  font-family: var(--rmd-font-sans);
}
a {
  color: var(--rmd-accent);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
/* The height of the bar, in one place, because the sidebar's height is measured from it. Change the
   bar's padding or its type and this has to follow — measure it, do not guess. */
:root {
  --masthead-h: 66px;
}
/* Sticky, and that is what makes the sidebar's arithmetic work.
   While it scrolled away, the sidebar sat at one offset at the top of the page and a different one
   once pinned, so no single height could be right in both — it was 49px past the bottom of the
   window until you had scrolled the bar off. Pinned, the offset is the same either way. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--rmd-bg);
  border-bottom: 1px solid var(--rmd-line);
  padding: 14px 24px;
}
.brand {
  font-weight: 700;
  font-size: 18px;
  color: var(--rmd-fg);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.brand-mark {
  width: 24px;
  height: 24px;
  display: block;
}
/* The drawer toggle and its close button are mobile-only; the breakpoint reveals them. */
.nav-toggle {
  display: none;
}
.sidebar-backdrop {
  display: none;
}
.drawer-close {
  display: none;
}
.search-close {
  display: none;
}
.body {
  display: flex;
  gap: 40px;
  max-width: 1120px;
  margin: 0 auto;
  padding: 32px 24px;
  align-items: flex-start;
}
/* The sidebar scrolls on its own.
   Sticky alone only pins the TOP of it: a list taller than the window then has its last entries
   below the fold, and the only way to reach them is to scroll the article to its end and keep
   going. Bounding the height and letting it scroll means the whole list is reachable wherever the
   page happens to be. `overscroll-behavior` stops that scroll from continuing into the page once
   the list ends, which is what makes the two feel separate rather than chained. */
nav {
  flex: 0 0 220px;
  position: sticky;
  top: calc(var(--masthead-h) + 24px);
  /* Same reason as the drawer's height below: the breakpoint is 860px, so a phone in landscape and
     a small tablet get THIS layout, and there `100vh` is still the height with the URL bar gone. */
  max-height: calc(100vh - var(--masthead-h) - 48px);
  max-height: calc(100dvh - var(--masthead-h) - 48px);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Room for the scrollbar, so it never sits on top of a link. */
  padding-right: 6px;
  /* Thin and quiet: the sidebar is chrome, and a full-weight scrollbar beside it reads as content. */
  scrollbar-width: thin;
  scrollbar-color: var(--rmd-line) transparent;
}
nav::-webkit-scrollbar {
  width: 8px;
}
nav::-webkit-scrollbar-thumb {
  background: var(--rmd-line);
  border-radius: 4px;
}
.sidebar-group h4 {
  margin: 20px 0 8px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--rmd-muted);
}
.sidebar-group ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.sidebar-group li {
  margin: 2px 0;
}
.sidebar-group .link {
  display: block;
  padding: 5px 10px;
  border-radius: 6px;
  color: var(--rmd-fg);
  font-size: 14px;
}
.sidebar-group .link:hover {
  background: var(--rmd-code-bg);
  text-decoration: none;
}
.sidebar-group .link.active {
  background: var(--rmd-code-bg);
  color: var(--rmd-accent);
  font-weight: 600;
}
.content {
  flex: 1;
  min-width: 0;
}
.notfound {
  padding: 2em 0;
}
.notfound h1 {
  font-size: 2.1rem;
  margin: 0 0 0.5em;
  letter-spacing: -0.02em;
}
.notfound p {
  max-width: 60ch;
  color: var(--rmd-fg);
}
.notfound p + p {
  margin-top: 1.2em;
}
article h1 {
  font-size: 2.1rem;
  line-height: 1.2;
  margin: 0 0 0.6em;
  letter-spacing: -0.02em;
}
/* A heading arrived at from a link stops BELOW the masthead, which is sticky and would otherwise
   cover it. The number is the bar's height plus a little air; `--masthead-h` keeps the two in step.
   Set on everything that carries an id, because that is what a fragment can name. */
article h1,
article h2,
article h3,
article h4 {
  scroll-margin-top: calc(var(--masthead-h) + 16px);
}
article h2 {
  font-size: 1.4rem;
  margin: 2em 0 0.6em;
  padding-top: 0.6em;
  border-top: 1px solid var(--rmd-line);
}
article h3 {
  font-size: 1.1rem;
  margin: 1.6em 0 0.5em;
}
/* Prose runs to the same edge as everything beside it.
 *
 * It used to stop at 70ch — 672px, measured — while the code blocks, tables and demos next to it ran
 * the full width of the column, so a word that would have fitted wrapped to the next line and every
 * paragraph ended short of the block above it. Two measures in one column read as a mistake even when
 * each is defensible on its own.
 *
 * The column is the measure now, and nothing here sets a second one: `.body` caps the page at 1120px,
 * the sidebar takes 226px and the gap 40px, which leaves about 806px — roughly 84 characters at this
 * font. That is wider than the classic 65–75 and deliberately so; it is also why no `max-width` in `ch`
 * appears below. A `ch` on a `<pre>` is measured in the mono font and a `ch` on a `<p>` is not, so the
 * same number would have produced two different widths and missed the point.
 */
/*
 * A long token in prose wraps rather than widening the page.
 *
 * The default `overflow-wrap: normal` breaks lines only at spaces, so one unbroken run of
 * characters — `this.user = focusOn(this.user).get("address").get("city").set(city)` on
 * /concepts/state, 64 characters of it — is wider than the column and the whole DOCUMENT scrolls
 * sideways to fit it. On a phone that moves every page, not just the paragraph.
 *
 * Set on the text elements rather than on `code` alone, because the same thing happens to a bare
 * URL, a long identifier or a package name written as plain prose. `anywhere` rather than
 * `break-word` because it also shrinks the element's min-content width, which is what a flex item
 * measures.
 *
 * A table CELL is deliberately not in this list. On a phone a `DataTable` gives every value cell a
 * fixed width so the columns line up down the page, and `anywhere` there would break words inside
 * an already narrow box — earlier, not later. The cells have their own wrapping story; this rule
 * is about prose that has none.
 *
 * Block code is deliberately NOT included: a `<pre>` scrolls inside its own box (`overflow-x: auto`
 * above), which keeps lines intact and indentation readable. Wrapping it would be worse than the
 * bug — it is code, and where the line ends is information.
 */
article p,
article li,
article dd,
article blockquote {
  overflow-wrap: anywhere;
}
article pre,
article pre code {
  overflow-wrap: normal;
}
article code {
  background: var(--rmd-code-bg);
  padding: 0.15em 0.4em;
  border-radius: 4px;
  font-size: 0.9em;
  font-family: var(--rmd-font-mono);
}
article pre {
  background: var(--rmd-code-bg);
  padding: 16px;
  border-radius: 8px;
  overflow-x: auto;
}
article pre code {
  background: none;
  padding: 0;
  font-size: 0.86rem;
}
.demo-counter {
  margin: 0;
}
.demo-counter button {
  font: inherit;
  padding: 8px 16px;
  border: 1px solid var(--rmd-line);
  border-radius: 8px;
  background: var(--rmd-bg);
  color: var(--rmd-fg);
  cursor: pointer;
}
.demo-counter button:hover {
  border-color: var(--rmd-accent);
}
ramonda-host[data-ramonda="Counter"],
div[data-ramonda="Counter"] {
  display: block;
  margin: 1.5em 0;
  padding: 20px;
  border: 1px solid var(--rmd-line);
  border-radius: 10px;
}

/* ── demos ─────────────────────────────────────────────────────────────── */
.demo {
  margin: 1.6em 0;
  border: 1px solid var(--rmd-line);
  border-radius: 10px;
  overflow: hidden;
}
.demo-title {
  margin: 0;
  padding: 12px 16px;
  font-size: 0.95rem;
  background: var(--rmd-code-bg);
  border-bottom: 1px solid var(--rmd-line);
}
.demo-stage {
  padding: 18px 16px;
}
.demo-stage > * {
  margin: 0;
}
.demo-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 8px;
}
.demo-row:last-child {
  margin-bottom: 0;
}
.demo-note {
  color: var(--rmd-muted);
  font-size: 0.85rem;
}
/* Long query strings (e.g. the lens demo's `last:` line) wrap instead of
   overflowing or getting clipped. */
.demo-note code {
  overflow-wrap: anywhere;
}
.demo-error {
  color: var(--rmd-error);
  margin: 0;
}
.demo-log {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  font-size: 0.85rem;
  color: var(--rmd-muted);
}
.demo-log li {
  padding: 2px 0;
}
/* Structural sharing, shown as identity: new object vs the very same one. */
.lens-fresh {
  color: var(--rmd-fresh);
}
.lens-shared {
  color: var(--rmd-ok);
}
.demo-badge {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.8rem;
  border: 1px solid var(--rmd-line);
}
/* Fixed on purpose: these two show a dark and a light badge together, so neither may follow the
   page. Their colours are the subject, not the chrome. */
.demo-badge-dark {
  background: #1b1b22;
  color: #eee;
}
.demo-badge-light {
  background: #fff5fa;
  color: #a03070;
}
.demo button {
  font: inherit;
  font-size: 0.9rem;
  padding: 6px 12px;
  border: 1px solid var(--rmd-line);
  border-radius: 7px;
  background: var(--rmd-bg);
  color: var(--rmd-fg);
  cursor: pointer;
}
.demo button:hover:not(:disabled) {
  border-color: var(--rmd-accent);
}
.demo button:disabled {
  opacity: 0.45;
  cursor: default;
}
.demo input {
  font: inherit;
  font-size: 0.9rem;
  padding: 6px 10px;
  border: 1px solid var(--rmd-line);
  border-radius: 7px;
  background: var(--rmd-bg);
  color: var(--rmd-fg);
}
.demo kbd {
  background: var(--rmd-code-bg);
  border: 1px solid var(--rmd-line);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 1px 7px;
  font-size: 0.85rem;
}
/* The star toggle is a glyph, not a boxed button. */
.demo button.star {
  border: 0;
  background: none;
  padding: 0 2px;
  font-size: 1rem;
  line-height: 1;
  color: var(--rmd-accent);
}
.demo button.star:hover:not(:disabled) {
  border: 0;
  transform: scale(1.15);
}
.demo-toggle {
  width: 100%;
  text-align: left;
  border: 0 !important;
  border-top: 1px solid var(--rmd-line) !important;
  border-radius: 0 !important;
  background: var(--rmd-code-bg) !important;
  color: var(--rmd-muted) !important;
  font-size: 0.8rem !important;
  padding: 8px 16px !important;
}
.demo-file {
  float: right;
  opacity: 0.7;
}
.examples .demo:first-child {
  margin-top: 0;
}

/* ── code blocks (copy button) ─────────────────────────────────────────── */
/* Wraps the Shiki <pre> so a copy button can sit over its top-right corner. */
.code-block {
  position: relative;
  margin: 1.4em 0;
}
.code-block pre {
  margin: 0;
  /**
   * Room for the copy button, which is absolutely positioned over this corner.
   *
   * It sits at `top: 8px` and stands 23.5px tall (0.72rem at line-height 1, plus
   * 5px of padding each side and a 1px border), so it reaches 31.5px down — while
   * the first line of code used to start at the 16px the padding gives every side.
   * A first line long enough to reach the corner ran underneath it and was hidden
   * by the button's own background, which is exactly when you are hovering to
   * click it.
   *
   * Derived from the button rather than guessed, so the two move together: 8 + 23.5
   * rounded up, plus a couple of px so the line is not touching it.
   */
  padding-top: 34px;
}
.copy-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 1;
  font: inherit;
  font-size: 0.72rem;
  line-height: 1;
  padding: 5px 9px;
  border: 1px solid var(--rmd-line);
  border-radius: 6px;
  background: var(--rmd-bg);
  color: var(--rmd-muted);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity 0.12s ease,
    color 0.12s ease,
    border-color 0.12s ease;
}
.code-block:hover .copy-btn,
.copy-btn:focus-visible {
  opacity: 1;
}
.copy-btn:hover {
  color: var(--rmd-accent);
  border-color: var(--rmd-accent);
}
/* Touch devices have no hover, so the button has to be there from the start. */
@media (hover: none) {
  .copy-btn {
    opacity: 0.85;
  }
}
/* The demo panel keeps its own framing: flush top border, capped height. */
.code-block-demo {
  margin: 0;
}
.code-block-demo pre {
  border-top: 1px solid var(--rmd-line);
  border-radius: 0;
  max-height: 420px;
}

/* ── screenshots in prose ───────────────────────────────────────────────── */

/**
 * The devtools page is the first page with images on it, and without this a 1600px capture simply
 * overflows the column. The border matters as much as the width: these are pictures of a dark panel on
 * a light page, and without an edge they bleed into the prose.
 */
article img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 1.6rem 0;
  border: 1px solid var(--rmd-line);
  border-radius: 8px;
  /* A screenshot of a panel is a thing on the page, not a diagram in it. */
  box-shadow: 0 2px 10px rgb(0 0 0 / 7%);
}

/* The badge GIF is one small element, not a screen — full width would blow it up to a blur. */
article img[src$=".gif"] {
  max-width: 300px;
  border-radius: 10px;
}

/* ── prose tables, lists, blockquotes ──────────────────────────────────── */
/* A DataTable is a real table until the screen is too narrow for one, at which point each row
   becomes a card: a full-width name bar that stays put, and the remaining cells scrolling under it
   with their column heading above each value. See src/DataTable.tsx for why the reflow is CSS. */
.table-wrap {
  margin: 1.4em 0;
}
.table-wrap table {
  border-collapse: collapse;
  font-size: 0.92rem;
  width: 100%;
}
.table-wrap th,
.table-wrap td {
  border: 1px solid var(--rmd-line);
  padding: 8px 12px;
  text-align: left;
  vertical-align: top;
}
.table-wrap th {
  background: var(--rmd-code-bg);
  font-weight: 600;
}

@media (max-width: 700px) {
  /* The heading row is gone, so it must not be read out either — `display: none` does both. Its
     text is not lost: it is on every cell as `data-label`. */
  .table-wrap thead {
    display: none;
  }
  /* ONE scroll container for the whole table, so every row's values move together and the columns
     stay lined up down the page. A scroller per row would drift out of step the moment two rows were
     scrolled by different amounts, and would put a scrollbar under each of them.

     The frame belongs HERE and not on the rows, and that is the difference between one table and a
     stack of small ones: a border on a row is inside the scrolled content, so its left and right
     edges slide away as you scroll. On the scroller it stays where the table stops.

     `container-type` is what lets the name bar below ask for the visible width. */
  .table-wrap {
    overflow-x: auto;
    container-type: inline-size;
    border: 1px solid var(--rmd-line);
    border-radius: 8px;
  }
  .table-wrap table,
  .table-wrap tbody {
    display: block;
    width: max-content;
    min-width: 100%;
  }
  /* A row is two bands, and nothing separates one row from the next but the change of colour —
     grey, white, grey, white, with no rule and no gap. `nowrap` stops a break BETWEEN the value
     cells, so they form the one long line that scrolls; each cell sets `normal` again for its text. */
  .table-wrap tr {
    display: block;
    white-space: nowrap;
    width: max-content;
    min-width: 100%;
  }
  /* The grey band: the name, pinned. `100cqw` and not `100%` — the row is as wide as its widest
     line, and a bar that width would carry the name off-screen with everything else. The container
     query unit is the width you can SEE, which is exactly where the name has to stay. */
  .table-wrap td:first-child {
    display: block;
    position: sticky;
    left: 0;
    z-index: 1;
    width: 100cqw;
    padding: 7px 12px;
    border: 0;
    background: var(--rmd-code-bg);
    font-weight: 600;
    white-space: normal;
  }
  /* The white band: the values, side by side, scrolling under the name.
     A FIXED width, not one from the content: every row is its own line of inline boxes, so nothing
     makes the second value of one row start where the second value of the next one does. With a
     single scrollbar moving all of them together, that misalignment is what you would notice —
     scroll a little and each row is showing a different column. */
  .table-wrap td:not(:first-child) {
    display: inline-block;
    vertical-align: top;
    white-space: normal;
    width: 11rem;
    padding: 6px 12px 8px;
    border: 0;
    border-left: 1px solid var(--rmd-line);
  }
  /* The column dividers scroll WITH the columns, because that is what they divide. The first one
     would sit on the table's own left edge. */
  .table-wrap td:nth-child(2) {
    border-left: 0;
  }
  /* One value column has nothing to line up with, so it takes the width instead of 11rem of it.
     The fixed width above is there to keep the second value of one row above the second value of
     the next; a two-column table has no second value, nothing scrolls, and the fixed width leaves
     the text wrapping inside 176px with the rest of the screen empty beside it — measured on
     /routing/params, whose table is `| name | what it gives you |`.
     `:nth-child(2):last-child` is exactly "the only value cell". */
  .table-wrap td:nth-child(2):last-child {
    width: 100cqw;
  }
  /* The column heading, in small type above its value. */
  .table-wrap td:not(:first-child)::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 3px;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--rmd-muted);
  }
  /* A heading with nothing under it is a blank line of small caps, so drop the cell entirely. */
  .table-wrap td:empty {
    display: none;
  }
}
article ul,
article ol {
  padding-left: 1.4em;
}
article li {
  margin: 0.3em 0;
}
article blockquote {
  margin: 1.2em 0;
  padding: 0.4em 0 0.4em 1em;
  border-left: 3px solid var(--rmd-line);
  color: var(--rmd-muted);
}
article hr {
  border: 0;
  border-top: 1px solid var(--rmd-line);
  margin: 2em 0;
}
article strong {
  font-weight: 650;
}

/* ── syntax highlighting (Shiki, dual theme via CSS variables) ─────────── */
/* Shiki stamps every token with --shiki-light and --shiki-dark. Reading them
   here means one build output serves both themes, with no second render and no
   JavaScript — the page just follows the reader's preference. */
.shiki,
.shiki span {
  color: var(--shiki-light);
}
@media (prefers-color-scheme: dark) {
  .shiki,
  .shiki span {
    color: var(--shiki-dark);
  }
}
/* Shiki ships its own theme background; the site's own is used instead so code
   blocks match the rest of the page in both themes. */
article pre.shiki {
  background: var(--rmd-code-bg) !important;
}
.shiki code {
  display: block;
}

/* ── search ────────────────────────────────────────────────────────────── */
.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.search-open {
  font: inherit;
  font-size: 0.85rem;
  padding: 6px 12px;
  border: 1px solid var(--rmd-line);
  border-radius: 8px;
  background: var(--rmd-bg);
  color: var(--rmd-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}
.search-open:hover {
  border-color: var(--rmd-accent);
  color: var(--rmd-fg);
}
.search-open kbd {
  background: var(--rmd-code-bg);
  border: 1px solid var(--rmd-line);
  border-radius: 4px;
  padding: 0 5px;
  font-size: 0.75rem;
}
.search {
  position: fixed;
  inset: 0;
  z-index: 50;
}
.search-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
}
.search-panel {
  position: relative;
  max-width: 640px;
  margin: 10vh auto 0;
  background: var(--rmd-bg);
  border: 1px solid var(--rmd-line);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
  overflow: hidden;
}
.search-input {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  padding: 16px 18px;
  border: 0;
  border-bottom: 1px solid var(--rmd-line);
  background: transparent;
  color: var(--rmd-fg);
  outline: none;
}
/* Suppress the native clear (✕) a type="search" input draws — the panel has its
   own close control on mobile, and two ✕ side by side read as a mistake. */
.search-input::-webkit-search-cancel-button,
.search-input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}
.search-note {
  margin: 0;
  padding: 18px;
  color: var(--rmd-muted);
  font-size: 0.9rem;
}
.search-results {
  list-style: none;
  margin: 0;
  padding: 6px;
  max-height: 60vh;
  overflow-y: auto;
}
.search-results li {
  margin: 0;
}
.search-result {
  display: block;
  padding: 10px 12px;
  border-radius: 8px;
  color: var(--rmd-fg);
}
.search-result:hover {
  background: var(--rmd-code-bg);
  text-decoration: none;
}
.search-result strong {
  display: block;
  font-size: 0.95rem;
  margin-bottom: 2px;
}
.search-excerpt {
  display: block;
  color: var(--rmd-muted);
  font-size: 0.83rem;
  line-height: 1.5;
}
.search-excerpt mark {
  background: transparent;
  color: var(--rmd-accent);
  font-weight: 600;
}
@media (max-width: 640px) {
  .search-open kbd {
    display: none;
  }
}

/* ── mobile: the sidebar becomes a drawer ──────────────────────────────── */
@media (max-width: 860px) {
  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
    line-height: 1;
    padding: 5px 10px;
    border: 1px solid var(--rmd-line);
    border-radius: 8px;
    background: var(--rmd-bg);
    color: var(--rmd-fg);
    cursor: pointer;
  }
  .nav-toggle:hover {
    border-color: var(--rmd-accent);
  }
  .masthead {
    padding: 12px 16px;
    gap: 10px;
  }
  /* The outlet takes the full width; the sidebar leaves the flow entirely. */
  .body {
    display: block;
    padding: 24px 20px;
  }
  nav {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 60;
    width: min(280px, 82vw);
    /* `dvh`, not `vh`: on a phone `100vh` is the LARGE viewport — the height with the URL bar
       retracted — so while the bar is showing, the bottom of the drawer sits below the screen and
       the last link in the list cannot be reached. It appeared only after scrolling the page
       behind the drawer, which is what retracts the bar. `dvh` is the height that is actually
       visible, and it follows the bar. The `vh` line above it is the fallback for a browser that
       does not know the unit, where the old behaviour is no worse than it was. */
    height: 100vh;
    height: 100dvh;
    /* The desktop rule caps the sidebar below the masthead, and the drawer is not below anything —
       it covers the screen. Left in place it cut the drawer short by exactly the masthead's height
       and left a gap under the links. */
    max-height: none;
    margin: 0;
    padding: 14px 20px 20px;
    background: var(--rmd-bg);
    border-right: 1px solid var(--rmd-line);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.18);
  }
  .body.nav-open nav {
    transform: translateX(0);
  }
  /* The X stays put; only the link list below it scrolls. */
  .drawer-close {
    display: block;
    align-self: flex-end;
    flex: 0 0 auto;
    margin: 0 -6px 4px 0;
    font-size: 1.1rem;
    line-height: 1;
    padding: 4px 10px;
    border: 0;
    background: none;
    color: var(--rmd-muted);
    cursor: pointer;
  }
  .drawer-close:hover {
    color: var(--rmd-accent);
  }
  nav .sidebar-inner {
    flex: 1 1 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 55;
    background: rgba(0, 0, 0, 0.4);
    /* A drag on the backdrop dismisses, it does not scroll what is underneath. `overflow: hidden`
       below stops the document's own scrolling; this stops the gesture from reaching it at all,
       which is the half `overflow` has never been reliable about on a phone. */
    touch-action: none;
  }

  /*
   * The page behind an open drawer does not move.
   *
   * Read from the class the RENDER already writes. `.body.nav-open` is `App`'s own markup — the
   * drawer's state, expressed where every other piece of it is expressed — and `:has()` is what
   * lets the document act on a class held by a descendant. Nothing writes to `documentElement`, so
   * there is no second copy of `menuOpen` to keep in step, no cleanup to forget on unmount, and no
   * class left behind when the component goes away.
   *
   * Inside this breakpoint on purpose: the drawer exists only here, so a wide window cannot lock a
   * page that has no drawer to explain it.
   *
   * `:has()` is Chrome 105, Safari 15.4 and Firefox 121. Older than that and the lock simply does
   * not apply — the drawer still opens, closes and scrolls; the page behind it moves as it did
   * before.
   */
  html:has(.body.nav-open),
  html:has(.body.nav-open) body {
    overflow: hidden;
  }
  /* The search modal fills the screen instead of floating in a card. */
  .search-panel {
    max-width: none;
    width: 100%;
    height: 100%;
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    display: flex;
    flex-direction: column;
  }
  .search-results {
    max-height: none;
    flex: 1 1 auto;
  }
  .search-close {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    z-index: 1;
    padding: 14px 18px;
    font-size: 1.2rem;
    line-height: 1;
    border: 0;
    background: none;
    color: var(--rmd-muted);
    cursor: pointer;
  }
  .search-input {
    padding-right: 56px;
  }
}

/* A form demo: inputs stacked with their labels, and rows that can be removed. */
.demo-form label {
  display: block;
  margin-bottom: 10px;
  font-size: 0.9rem;
}
.demo-form label input {
  display: block;
  width: 100%;
  max-width: 340px;
  margin-top: 4px;
  font: inherit;
  font-size: 0.9rem;
  padding: 6px 9px;
  border: 1px solid var(--rmd-line);
  border-radius: 7px;
  background: var(--rmd-bg);
  color: var(--rmd-fg);
}
.demo-form input[aria-invalid="true"] {
  border-color: var(--rmd-error);
}
.demo-form fieldset {
  border: 1px solid var(--rmd-line);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0 0 12px;
}
.demo-form legend {
  font-size: 0.85rem;
  color: var(--rmd-muted);
  padding: 0 4px;
}
.demo-rows {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
}
.demo-rows li {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.demo-rows input {
  font: inherit;
  font-size: 0.9rem;
  padding: 5px 9px;
  border: 1px solid var(--rmd-line);
  border-radius: 7px;
  background: var(--rmd-bg);
  color: var(--rmd-fg);
}
.demo-ok {
  color: var(--rmd-ok);
  margin: 0 0 8px;
}
