/* Paramsat. One stylesheet, written by hand; it reproduces the Next.js build's
 * look (Tailwind utilities and the typography plugin, which it replaces).
 * Light by default, dark via prefers-color-scheme. */

/* Tokens */
:root {
  --background: #ffffff;
  --foreground: #18181b;
  --muted: #6b7280;
  --border: #e5e7eb;
  --sidebar: #fafafa;
  --hover: #f1f1f4;
  --accent: #5a3ee0;
  --accent-low: #ece6ff;
  --accent-high: #2a1d80;
  --code: #f6f8fa;
  --ok: #16833a;
  --warn: #b45309;

  /* The system fonts pluto-lang.dev uses: nothing to download. */
  --font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;

  /* The project frame: sidebar, the content pad either side of the column,
   * the column and the table-of-contents rail. The header is laid out from
   * the same numbers, so the search sits over the column on every page. The
   * pad and the rail change at the breakpoints at the end. */
  --sidebar-width: 16rem;
  --content-pad: 1.25rem;
  --column-width: 48rem;
  --rail-width: 0rem;
  /* A column of a row of columns keeps at least this width, or the row
   * stacks; the columns stand this far apart (internal/markdown,
   * columns.go). Cards keep the same minimum. */
  --column-min: 12.5rem;
  --column-gap: 1.5rem;

  /* Code, in the GitHub palette the Shiki themes used. */
  --code-text: #24292e;
  --code-comment: #6a737d;
  --code-keyword: #d73a49;
  --code-string: #032f62;
  --code-constant: #005cc5;
  --code-function: #6f42c1;
  --code-tag: #22863a;
  --code-deleted: #b31d28;
  --code-deleted-bg: #ffeef0;
  --code-inserted: #22863a;
  --code-inserted-bg: #f0fff4;

  --kbd: #18181b;
  --kbd-shadow: rgb(24 24 27 / 10%);

  /* Starlight variable names used by inline SVG diagrams migrated from
   * pluto-lang.dev, mapped onto this theme so the diagrams keep their colours. */
  --sl-color-text: var(--foreground);
  --sl-color-gray-3: var(--muted);
  --sl-font: var(--font-sans), system-ui, sans-serif;
  --sl-font-mono: var(--font-mono), ui-monospace, monospace;
  --pluto-icon-lime: var(--tone-lime);
  --pluto-icon-pink: var(--tone-pink);

  /* The tones icons are drawn in (package icons). Each clears 3:1 against
   * the page, the floor for marks that are not text: lime and amber take a
   * darker step on white to get there. */
  --tone-sky: #0284c7;
  --tone-green: #16a34a;
  --tone-purple: #9333ea;
  --tone-cyan: #0891b2;
  --tone-emerald: #059669;
  --tone-lime: #4d7c0f;
  --tone-blue: #2563eb;
  --tone-orange: #ea580c;
  --tone-rose: #e11d48;
  --tone-amber: #d97706;
  --tone-pink: #db2777;
  --tone-fuchsia: #c026d3;
  --tone-indigo: #4f46e5;
  --tone-accent: var(--accent); /* the mark's own colour, in either theme */
  color-scheme: light;
  /* A popover's own background and shadow: the page's in the light, and in
   * the dark a shade above the page with a deeper shadow, since a shadow
   * cannot show on a near-black page. */
  --popover: var(--background);
  --popover-shadow: 0 8px 24px rgb(0 0 0 / 0.08);
}

/* Dark, by the system's own scheme unless the reader has chosen, and by the
 * choice: the two blocks below are one set of tokens, which a test keeps
 * equal (internal/web). The choice is data-theme on the root, set before the
 * first paint by the few lines in the page's head from what the reader
 * picked in the header's theme select. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --background: #101014;
    --popover: #1b1b21;
    --popover-shadow: 0 8px 24px rgb(0 0 0 / 0.6);
    --foreground: #e6e6ea;
    --muted: #9a9aa6;
    --border: #27272e;
    --sidebar: #15151a;
    --hover: #1e1e25;
    --accent: #8b6fff;
    --accent-low: #221c40;
    --accent-high: #c4b5fd;
    --code: #1a1a20;
    --ok: #4ade80;
    --warn: #fbbf24;

    --code-text: #e1e4e8;
    --code-comment: #6a737d;
    --code-keyword: #f97583;
    --code-string: #9ecbff;
    --code-constant: #79b8ff;
    --code-function: #b392f0;
    --code-tag: #85e89d;
    --code-deleted: #fdaeb7;
    --code-deleted-bg: #86181d;
    --code-inserted: #85e89d;
    --code-inserted-bg: #144620;

    --kbd: #ffffff;
    --kbd-shadow: rgb(255 255 255 / 10%);

    --tone-sky: #38bdf8;
    --tone-green: #4ade80;
    --tone-purple: #c084fc;
    --tone-cyan: #22d3ee;
    --tone-emerald: #34d399;
    --tone-lime: #a3e635;
    --tone-blue: #60a5fa;
    --tone-orange: #fb923c;
    --tone-rose: #fb7185;
    --tone-amber: #fbbf24;
    --tone-pink: #f472b6;
    --tone-fuchsia: #e879f9;
    --tone-indigo: #818cf8;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --background: #101014;
  --popover: #1b1b21;
  --popover-shadow: 0 8px 24px rgb(0 0 0 / 0.6);
  --foreground: #e6e6ea;
  --muted: #9a9aa6;
  --border: #27272e;
  --sidebar: #15151a;
  --hover: #1e1e25;
  --accent: #8b6fff;
  --accent-low: #221c40;
  --accent-high: #c4b5fd;
  --code: #1a1a20;
  --ok: #4ade80;
  --warn: #fbbf24;

  --code-text: #e1e4e8;
  --code-comment: #6a737d;
  --code-keyword: #f97583;
  --code-string: #9ecbff;
  --code-constant: #79b8ff;
  --code-function: #b392f0;
  --code-tag: #85e89d;
  --code-deleted: #fdaeb7;
  --code-deleted-bg: #86181d;
  --code-inserted: #85e89d;
  --code-inserted-bg: #144620;

  --kbd: #ffffff;
  --kbd-shadow: rgb(255 255 255 / 10%);

  --tone-sky: #38bdf8;
  --tone-green: #4ade80;
  --tone-purple: #c084fc;
  --tone-cyan: #22d3ee;
  --tone-emerald: #34d399;
  --tone-lime: #a3e635;
  --tone-blue: #60a5fa;
  --tone-orange: #fb923c;
  --tone-rose: #fb7185;
  --tone-amber: #fbbf24;
  --tone-pink: #f472b6;
  --tone-fuchsia: #e879f9;
  --tone-indigo: #818cf8;
}

/* Base */
*,
::before,
::after,
::backdrop {
  box-sizing: border-box;
  border: 0 solid;
  margin: 0;
  padding: 0;
}

html {
  height: 100%;
  line-height: 1.5;
  font-family: var(--font-sans);
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  scrollbar-gutter: stable;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans), system-ui, sans-serif;
}

h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; }
a { color: inherit; text-decoration: inherit; }
b, strong { font-weight: bolder; }
code, kbd, samp, pre { font-family: var(--font-mono); font-size: 1em; }
small { font-size: 80%; }
/* Underlined words, <u>, are underlined through their descenders, the line
 * unbroken as a word processor draws it (Tejas, 22 Oct 2026): the browser's
 * own breaks it at each p, g or y. A link keeps the browser's. */
u { text-decoration-skip-ink: none; }
sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; }
sub { bottom: -0.25em; }
sup { top: -0.5em; }
hr { height: 0; color: inherit; border-top-width: 1px; }
table { text-indent: 0; border-color: inherit; border-collapse: collapse; }
summary { display: list-item; }
summary::-webkit-details-marker { display: none; }
ol, ul, menu { list-style: none; }
img, svg, video, canvas, audio, iframe, embed, object { display: block; vertical-align: middle; }
img, video { max-width: 100%; height: auto; }
button, input, select, optgroup, textarea {
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  background-color: transparent;
  border-radius: 0;
  opacity: 1;
}
button { appearance: button; }
::placeholder { opacity: 1; color: color-mix(in oklab, currentcolor 50%, transparent); }
::-webkit-search-decoration { -webkit-appearance: none; }
[hidden]:where(:not([hidden="until-found"])) { display: none !important; }

/* The mark and the wordmark (assets/logo/README.md): the bindu in the accent,
 * the name as live text in the site's font. Beside words the mark is set in
 * their line, its centre on the middle of their lowercase letters, which is
 * where the eye finds the middle of a word: vertical-align: middle is the
 * baseline plus half the font's own x-height. The middle of the line sits
 * higher, and a mark centred there looks raised. In the wordmark the mark
 * overhangs its line rather than deepening it, so the reading under the name
 * keeps its place: the margins take equal amounts off top and bottom, which
 * leaves the middle where it was. */
.mark { flex-shrink: 0; color: var(--accent); }
.wordmark { font-weight: 500; letter-spacing: -0.01em; white-space: nowrap; }
.wordmark .mark { display: inline-block; width: 1.25em; height: 1.25em; margin: -0.2em 0.4em -0.2em 0; vertical-align: middle; }
/* The bindu between the words, the font's bullet, a little nearer "sat" than
 * its even margins would set it: beside it, the "m" stands straight and the
 * "s" curves away, and at the dot's height there was 2px more room on the
 * side of "sat" (Tejas, 23 Oct 2026). 0.03em more on its left and less on its
 * right even the two there, the word as wide as it was. */
.wordmark .bindu { margin: 0 0.01em 0 0.07em; color: var(--accent); }
.lockup { margin-bottom: 2.5rem; }
.lockup .wordmark { font-size: 1.5rem; line-height: 2rem; }
.lockup-reading { display: block; margin-left: 2.475rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); } /* under "param" */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Icons (package icons): sized by the text beside them, drawn in their tone,
 * or the text's colour without one. An emoji in an icon's place is text. */
.icon { flex-shrink: 0; width: 1.25em; height: 1.25em; }
.sidebar .avatar { flex-shrink: 0; width: 1.25em; height: 1.25em; } /* a Lab in the front page's list */
.icon-text { display: inline-flex; align-items: center; justify-content: center; font-size: 1.05em; line-height: 1; }
/* Beside words an icon's middle sits on the middle of their capitals, since
 * words open with a capital, right beside the icon; the wordmark, all
 * lowercase, keeps the lowercase middle. The row aligns by baselines, which
 * stands the icon on the words' baseline, and translate lowers it by half its
 * height less half the cap height (half the x-height without cap); the top
 * margin keeps it from deepening the row. An icon, or a group's chevron,
 * keeps to the first line of words that wrap, the words in a column of their
 * own, clear of it. */
:is(.sidebar-landing, .sidebar-link, .sidebar-group > summary, .feature-title, .page-title.has-icon, .project-card h3, .topic-projects > li, .artifact-list > li.has-icon, .edit-link a, .site-menu-account :is(a, button)) {
  align-items: baseline;
}
:is(.sidebar-landing, .sidebar-link, .sidebar-group > summary, .feature-title, .page-title.has-icon, .project-card h3, .topic-projects > li, .artifact-list > li.has-icon, .edit-link a, .site-menu-account :is(a, button)) > :is(svg.icon, .avatar, .page-icon, .sidebar-chevron) {
  margin-top: -1em;
  translate: 0 calc(50% - 0.5ex);
  translate: 0 calc(50% - 0.5cap);
}
.tone-sky { color: var(--tone-sky); }
.tone-green { color: var(--tone-green); }
.tone-purple { color: var(--tone-purple); }
.tone-cyan { color: var(--tone-cyan); }
.tone-emerald { color: var(--tone-emerald); }
.tone-lime { color: var(--tone-lime); }
.tone-blue { color: var(--tone-blue); }
.tone-orange { color: var(--tone-orange); }
.tone-rose { color: var(--tone-rose); }
.tone-amber { color: var(--tone-amber); }
.tone-pink { color: var(--tone-pink); }
.tone-fuchsia { color: var(--tone-fuchsia); }
.tone-indigo { color: var(--tone-indigo); }
.tone-accent { color: var(--tone-accent); }

/* Shared text */
.eyebrow {
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  color: var(--muted);
}
.page-title { font-size: 1.875rem; line-height: 2.25rem; font-weight: 600; letter-spacing: -0.025em; }
.lead { margin-top: 0.5rem; font-size: 1.125rem; line-height: 1.75rem; color: var(--muted); }
.list { margin-top: 0.5rem; }
.empty { font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }

/* Header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  border-bottom: 1px solid var(--border);
  background-color: color-mix(in oklab, var(--background) 95%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.site-header-inner {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: 100%;
  height: 3.5rem;
  padding-inline: 1rem;
}
/* The header holds its two ends at every width: the mark, which leads to
 * the front page, at the start, and the visitor's own account at the end,
 * "Sign in" until they are in and their picture after, with the search
 * between them. Nothing changes
 * sides as the header narrows, so a phone shows the same header, pared
 * down: the breadcrumb goes down into the menu bar with the sidebar. The far
 * end is the site's, not the Lab's, as on GitHub; Topics, the site's page,
 * is reached from the sidebars and the chips rather than from here.
 *
 * The brand: the mark, the Lab, and the project with its icon. Each link
 * holds one line of the same height, so they share a baseline, and their
 * pictures are set in that line at the level icons take beside words (see
 * the icons). The owner is pictured beside the Lab's name, as on GitHub or
 * Hugging Face, the picture an image of its own file, its rounded square
 * filling its box. The mark and the picture are drawn to the edges of a 27px box, the
 * project's icon, a line drawing with room inside its own square, to a 24px
 * one, so that all three can be seen; each overhangs its 20px line rather
 * than deepening it, the margins taking equal amounts off top and bottom,
 * so each box's middle, and so its level, holds. The brand's column is the
 * sidebar's width, and "Tejas's Lab / Paramsat" fills it at 1024px with the
 * pictures this size: the mark keeps its room, 8px to its rule and 10px from
 * the rule to the picture, so the spaces round the slash are 6px, a picture
 * stands 0.3em from its name, and the brand ends 0.5rem before the search. */
.site-brand { display: flex; align-items: center; gap: 0.375rem; min-width: 0; }
.site-home { flex-shrink: 0; }
.site-lab { min-width: 0; font-weight: 600; letter-spacing: -0.025em; }
.site-lab, .site-project { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The names clip their boxes for the ellipsis, and clipping stops at the
 * padding edge, so the pictures that overhang the line would lose their top
 * and bottom: the overhang is given as padding and taken back as margin. */
.site-lab, .site-project { padding-block: 0.21875rem; margin-block: -0.21875rem; }
.site-brand :is(.mark, .avatar, .icon) {
  display: inline-block;
  width: 1.6875rem;
  height: 1.6875rem;
  margin-block: -0.21875rem;
  vertical-align: middle;
  vertical-align: calc(0.5cap - 0.625rem);
}
.site-brand .icon { width: 1.5rem; height: 1.5rem; margin-block: -0.125rem; }
.site-lab .avatar, .site-project .icon { margin-right: 0.3em; }
.site-sep { color: var(--muted); }
.site-project { font-weight: 500; }
.site-search { flex: 1; min-width: 0; }
.site-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-left: auto;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted);
}
/* The far end's one control: the set's picture for nobody yet for a visitor,
 * the signed-in person's own once they are in, drawn to the edges of the
 * brand's 27px box,
 * opening the site's menu, a popover under the header's rule. A popover is
 * display none until open, so a browser with no popovers, which ignores
 * :popover-open, shows none stuck on the page; such a browser is shown a
 * plain Sign in link in the control's place instead. */
.site-account { display: flex; margin: -0.5rem; padding: 0.5rem; border: 0; border-radius: 0.375rem; background: none; color: var(--muted); cursor: pointer; }
.site-account :is(.avatar, .icon) { display: block; width: 1.6875rem; height: 1.6875rem; }
.site-signin { display: none; white-space: nowrap; }
@supports not selector(:popover-open) {
  .site-account { display: none; }
  .site-signin { display: inline; }
}
[popover] { display: none; }
[popover]:popover-open { display: block; }
.site-menu {
  position: fixed;
  inset: auto 1rem auto auto;
  top: calc(3.5rem + 0.5rem);
  width: min(20rem, calc(100vw - 2rem));
  margin: 0;
  padding: 0.375rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--popover);
  color: var(--foreground);
  box-shadow: var(--popover-shadow);
  font-size: 0.875rem;
  line-height: 1.25rem;
}
.site-menu-head { padding: 0.375rem 0.625rem 0.125rem; font-size: 0.75rem; line-height: 1rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.025em; color: var(--muted); }
.site-menu-theme { display: flex; gap: 0.25rem; padding: 0 0.375rem 0.375rem; }
.site-menu-theme button { flex: 1; padding: 0.3125rem 0; border: 1px solid var(--border); border-radius: 0.375rem; background: none; font: inherit; color: var(--foreground); cursor: pointer; }
.site-menu-theme button[aria-pressed="true"] { border-color: var(--accent-low); background: var(--accent-low); color: var(--accent-high); font-weight: 600; }
.site-menu-account { margin-top: 0.375rem; padding-top: 0.375rem; border-top: 1px solid var(--border); }
.site-menu-account :is(a, button):not(.action) { display: flex; gap: 0.5em; width: 100%; padding: 0.375rem 0.625rem; border: 0; border-radius: 0.375rem; background: none; font: inherit; color: var(--foreground); text-align: left; cursor: pointer; }
.site-menu-account :is(a, button) .icon { width: 1.25em; height: 1.25em; color: var(--muted); }
.site-menu-account p { padding: 0.25rem 0.625rem 0.5rem; color: var(--muted); }
/* A sign-out that failed, said under its button (parts.html). */
.signout-problem, .site-menu-account p.signout-problem { margin: 0.25rem 0 0; color: var(--warn); }
.site-menu-account .action { display: block; margin: 0 0.375rem 0.125rem; text-align: center; }

/* Edit, under an artifact or a Landing, for the Lab's owner; its pen is
 * levelled with the words as every icon is (see the icons). */
.edit-link { margin-top: 2rem; font-size: 0.875rem; line-height: 1.25rem; }
.edit-link a { display: inline-flex; gap: 0.375rem; color: var(--muted); }
.edit-link .icon { width: 1em; height: 1em; }
/* A source shown as it is: on the Edit page, the newer text a refused write
 * shows above the author's. */
.source {
  margin-top: 1.5rem;
  padding: 1rem;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--code);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  white-space: pre;
  tab-size: 4;
}
/* The Edit page's form: the source in a textarea the column's width, in the
 * code's face, saved as a draft as the author types (edit.js), which hides
 * Save, the button a browser without the script saves with, except beside
 * a conflict, where keeping one's own text as it is takes a click; the
 * line saying what the draft is, the note and Publish; a refused write's
 * notice, and the newer text above the author's. */
.editor { margin-top: 1.5rem; }
.editor-source {
  display: block;
  width: 100%;
  min-height: 60vh;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--code);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  tab-size: 4;
  resize: vertical;
}
.editor-source:focus-visible, .editor-note:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.editor-status { min-height: 1.25rem; margin-top: 0.5rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }
.editor-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-top: 1rem; }
.editor-actions .action { cursor: pointer; }
.edit-bar.autosave:not([data-conflict]):not([data-kept]):not([data-unsaved]) .editor-save { display: none; }
.editor-note { flex: 1 1 14rem; min-width: 0; min-height: 2.5rem; padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: 0.5rem; font-size: 0.875rem; line-height: 1.25rem; }
.editor-notice { margin-top: 1.5rem; padding: 0.75rem 1rem; border: 1px solid var(--border); border-radius: 0.5rem; color: var(--warn); }
.editor-detail { margin-top: 0.5rem; white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--font-mono); font-size: 0.8125rem; line-height: 1.5; color: var(--foreground); }
:is(.editor-newer h2, .editor-yours) { margin-top: 1.5rem; font-size: 1rem; line-height: 1.5rem; font-weight: 600; }
.editor-newer .source { margin-top: 0.5rem; max-height: 40vh; }
.editor-yours + * + .editor-source { margin-top: 0.5rem; }
/* Editing in place, for the owner: each block of the body, marked with
 * the lines of the source it came from, opens as those lines when its text
 * is clicked (edit.js), in the code's face in its place, and a faint dashed
 * line shows a pointer which block a click would open; the toolbar under
 * the header inserts blocks, says what the page shows and publishes it. */
.block-body[data-editable] [data-lines] { cursor: text; }
.block-body[data-editable] a[href] { cursor: pointer; }
.block-source {
  display: block;
  width: 100%;
  margin-block: 1rem;
  padding: 0.75rem 1rem;
  overflow: hidden;
  border: 1px solid var(--accent);
  border-radius: 0.5rem;
  background: var(--code);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  tab-size: 4;
  resize: none;
}
.block-source:focus-visible { outline: 2px solid var(--accent-low); outline-offset: 0; }
.editing-problem { margin-top: 1.5rem; padding: 0.75rem 1rem; border: 1px solid var(--border); border-radius: 0.5rem; font-size: 0.875rem; color: var(--warn); }
/* A draft its owner is shown, which readers are not. */
.page-hidden { margin-top: 1.25rem; padding: 0.75rem 1rem; border: 1px dashed var(--border); border-radius: 0.5rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }
/* The owner's toolbar on a page they edit in place (docs/plan-postgres.md,
 * decision 25): in the header's place while the page is edited (decision
 * 32, below), under it on Edit Source and an older revision, as wide as the
 * header and in its glass; its buttons the
 * Pluto playground's, small icons named by tooltips and the lead action a
 * glyph and its word. It holds every tool, as a word processor's does:
 * Insert and the open part's ↑, ↓, trash and ✓, greyed while nothing is
 * open, then the page's status, ↺ (Discard Draft), Edit Source and Publish.
 * On the header's own grid at the end of this sheet, Insert starts at the
 * column's left edge, under the search, and the far end ends under the
 * account. What sticks under the header sticks under the bar too, and a
 * heading jumped to stops below both. On a phone the script keeps the bar at the
 * top of what is shown while a part is typed in (docked), moved by the
 * visual viewport, as the browser moves the page, the header with it, under
 * its keyboard. */
:root { --edit-bar: 2.75rem; }
.edit-bar {
  position: sticky;
  top: 3.5rem;
  z-index: 19;
  border-bottom: 1px solid var(--border);
  background-color: color-mix(in oklab, var(--background) 95%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  font-size: 0.875rem;
  line-height: 1.25rem;
}
.edit-bar-inner { display: flex; align-items: center; height: var(--edit-bar); padding-inline: 1rem; }
/* Where the bar is narrower than what it holds, the status's words give
 * way first, cut short, then the tools before the far end, which scroll
 * sideways, each its own width; the far end never shrinks, so that Publish
 * is always on the screen and the page never scrolls sideways (the reviews
 * of 0828337). The row runs the bar's height, and the tools' box reaches
 * 0.25rem past them, so that it has room for a focused tool's ring. */
.edit-bar-row { display: flex; flex: 1; align-self: stretch; align-items: center; justify-content: space-between; gap: 1rem; min-width: 0; container-type: inline-size; }
.edit-bar-tools, .edit-bar-end { display: flex; align-items: center; gap: 0.25rem; min-width: 0; }
.edit-bar-tools { align-self: stretch; margin-inline: -0.25rem; padding-inline: 0.25rem; overflow: auto hidden; scrollbar-width: none; }
.edit-bar-tools .edit-bar-button { flex-shrink: 0; }
.edit-bar-tools::-webkit-scrollbar { display: none; }
.edit-bar-end { flex: 1 0 auto; justify-content: flex-end; gap: 0.375rem; }
.edit-bar-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 2rem;
  padding: 0 0.625rem;
  border: 0;
  border-radius: 0.375rem;
  background: none;
  color: var(--foreground);
  font: inherit;
  white-space: nowrap;
  cursor: pointer;
}
.edit-bar-icon { width: 2rem; padding: 0; }
.edit-bar-button[disabled] { opacity: 0.35; cursor: default; }
.edit-bar-button:has(+ .edit-pop:popover-open) { background: var(--hover); }
/* The text's tools, while the rich editor has the page (decision 26): the
 * style of the caret's blocks, as wide as its longest name so that the bar
 * keeps still as the caret moves, then bold, italic, underline, the link,
 * the ☺ and at their end More Formatting, between rules. A tool pressed, its
 * mark on the words selected or the caret in a link, is tinted as the menu's
 * chosen theme is. */
.edit-text-tools { display: none; }
.edit-bar.rich .edit-text-tools { display: contents; }
.edit-sep { align-self: center; width: 1px; height: 1.25rem; margin-inline: 0.125rem; background: var(--border); }
.edit-style { justify-content: space-between; gap: 0.375rem; width: 8rem; } /* "Numbered List", its longest name, fits */
.edit-style-glyph { display: none; font-size: 1.0625rem; line-height: 1; }
.edit-chevron { font-size: 0.75em; color: var(--muted); }
.edit-bold { font-weight: 700; }
.edit-italic { font-family: Georgia, "Times New Roman", serif; font-style: italic; }
.edit-underline { text-decoration: underline; text-underline-offset: 0.15em; }
.edit-bar-button:is([aria-pressed="true"], [data-active]) { background: var(--accent-low); color: var(--accent-high); }
/* The way back to the page, on the Edit page, where the tools would be. */
.edit-back { gap: 0.375rem; }
/* The open part's tools, after Insert and a rule. */
.edit-part-tools:not([hidden]) { display: contents; }
/* A part chosen, the title's picture or a picture in the text (edit.js,
 * decision 38): the text's tools give way to its own, its details, Picture,
 * and Done, and ↑ and ↓ where it moves, a picture in the text; its × takes
 * it away, so the bar holds no trash for it. */
.edit-bar.rich.choosing .edit-text-tools { display: none; }
.edit-bar:not(.choosing) .edit-part-details { display: none; }
.edit-bar.choosing .edit-part-tools [data-tool="delete"] { display: none; }
.edit-bar.choosing:not(.movable) .edit-part-tools :is([data-tool="up"], [data-tool="down"]) { display: none; }
.edit-bar.rich.choosing.movable .edit-part-tools :is([data-tool="up"], [data-tool="down"]) { display: inline-flex !important; }
.edit-part-details { gap: 0.375rem; }
.edit-part-details svg { flex-shrink: 0; width: 1.125rem; height: 1.125rem; color: var(--muted); }
/* Its details, in a panel beside the page, which stays as readers see it,
 * where the page has a margin for it, else in a sheet from the foot of what
 * is shown; the script places it. */
.edit-panel { position: fixed; inset: auto; margin: 0; padding: 0.875rem; border: 1px solid var(--border); border-radius: 0.75rem; background: var(--popover); color: var(--foreground); box-shadow: var(--popover-shadow); font-size: 0.875rem; line-height: 1.25rem; overflow-y: auto; }
.edit-panel:popover-open { display: grid; gap: 0.75rem; align-content: start; }
.edit-panel h2 { display: flex; align-items: center; gap: 0.5rem; margin: 0; font-size: 0.875rem; line-height: 1.25rem; font-weight: 600; }
.edit-panel h2 svg { width: 1.125rem; height: 1.125rem; color: var(--muted); }
.edit-panel label { display: grid; gap: 0.25rem; font-size: 0.8125rem; line-height: 1.25rem; color: var(--muted); }
.edit-panel label[hidden] { display: none; }
.edit-panel .editor-note { width: 100%; color: var(--foreground); }
.edit-panel select.editor-note { appearance: auto; }
.edit-panel p { margin: 0; font-size: 0.8125rem; line-height: 1.25rem; color: var(--muted); }
.edit-panel.sheet { padding: 0.5rem 1rem 1.25rem; border-width: 1px 0 0; border-radius: 1rem 1rem 0 0; box-shadow: 0 -8px 24px rgb(0 0 0 / 0.12); }
.edit-panel.sheet::before { content: ""; justify-self: center; width: 2.5rem; height: 0.3rem; margin-bottom: 0.25rem; border-radius: 1rem; background: var(--border); }
.edit-part-tools:not([hidden])::before { content: ""; align-self: center; width: 1px; height: 1.25rem; margin-inline: 0.125rem; background: var(--border); }
/* An emoji, or a glyph in the text's face, as the playground's icons. */
.edit-glyph { font-size: 1.0625rem; line-height: 1; }
/* The status takes the room the far end has left, up to its words' width,
 * and is cut short where there is less. A save that failed says so on a
 * line of its own under the bar, at every width, its words whole and Sign In
 * after them where the session ended (the review of 0406a70: at 1152px the
 * words were cut short in the bar, Sign In with them), as on a phone. */
.edit-bar .editor-status { flex: 1 1 0; width: 0; max-width: max-content; min-width: 0; margin: 0 0.375rem 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.edit-bar .editor-status[data-problem] { position: absolute; inset: 100% 0 auto; width: auto; max-width: none; margin: 0; padding: 0.375rem 1rem; border-bottom: 1px solid var(--border); background: var(--background); white-space: normal; }
/* A text this browser kept for the page, no save having taken it, offered
 * back as the page opens (edit.js's offerKept, Tejas, 21 Oct 2026): a
 * question, Restore or Discard, closed by a click outside or Escape without
 * discarding, and after it, while the page is open, a line under the bar in
 * the failed save's place, which a failed save, more pressing, covers. */
/* Under a failed save's line where one is said (edit.js's under). */
.edit-bar .kept-line { position: absolute; top: calc(100% + var(--under, 0px)); left: 0; right: 0; margin: 0; padding: 0.375rem 1rem; border-bottom: 1px solid var(--border); background: var(--background); color: var(--foreground); }
.kept-line button { padding: 0; border: 0; background: none; color: var(--accent); font: inherit; font-weight: 500; text-decoration: underline; text-underline-offset: 0.15em; cursor: pointer; }
.kept-line button:last-child { color: var(--muted); }
.kept-line button:disabled { opacity: 0.5; cursor: default; }
/* A copy that could not be made, said in the question (edit.js). */
.kept-problem { margin: 0 0 0.75rem; color: var(--warn); }
.kept-line .kept-problem { margin: 0 0 0 0.5em; }
.kept-dialog { max-width: min(26rem, calc(100vw - 2rem)); margin: auto; padding: 0; border: 1px solid var(--border); border-radius: 0.75rem; background: var(--background); color: var(--foreground); box-shadow: 0 12px 40px rgb(0 0 0 / 0.18); }
.kept-dialog::backdrop { background: rgb(0 0 0 / 0.25); }
.kept-dialog-body { padding: 1.25rem 1.25rem 1rem; }
.kept-dialog h2 { margin: 0 0 0.5rem; font-size: 1.0625rem; font-weight: 600; line-height: 1.4; }
.kept-dialog p { margin: 0 0 1rem; color: var(--muted); font-size: 0.9375rem; line-height: 1.45; }
.kept-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }
/* The page's revisions (decision 28): ‹, the number and › in one rounded
 * box, the number with the page's ● while the page shown is its unpublished
 * changes, a step greyed where there is nothing to step to. The number
 * opens the menu of them all. */
.rev { display: inline-flex; flex-shrink: 0; align-items: stretch; height: 2rem; margin-right: 0.25rem; overflow: hidden; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--background); }
.rev-step, .rev-number { display: inline-flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--foreground); font: inherit; text-decoration: none; cursor: pointer; }
.rev-step { width: 1.5rem; }
.rev-step:not([href]) { opacity: 0.3; cursor: default; }
.rev-step svg { width: 0.875rem; height: 0.875rem; }
.rev-number { gap: 0.3rem; min-width: 1.75rem; padding: 0 0.25rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.rev :focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.rev:has(+ .rev-menu:popover-open) .rev-number { background: var(--hover); }
/* The menu: each revision's number, its note and when it was published,
 * newest first, the one shown ticked, the unpublished changes first while
 * there are any. */
.rev-menu:popover-open { display: grid; gap: 0.125rem; width: 21rem; max-height: calc(100vh - 3.5rem - var(--edit-bar) - 1.5rem); overflow-y: auto; }
/* A row is its revision's link, laid over the whole row; a copy of an older
 * revision names it under its note, a link of its own above the row's. */
.rev-row { position: relative; display: grid; grid-template-columns: 1rem 2.25rem minmax(0, 1fr); column-gap: 0.375rem; padding: 0.4375rem 0.5rem; border-radius: 0.375rem; }
.rev-row[hidden] { display: none; }
.rev-open { grid-column: 1 / -1; grid-row: 1; display: grid; grid-template-columns: subgrid; align-items: baseline; color: var(--foreground); text-decoration: none; }
.rev-open::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.rev-open:focus-visible { outline: none; }
.rev-open:focus-visible::after { outline: 2px solid var(--accent); outline-offset: -2px; }
.rev-row:has([aria-current]) { background: var(--hover); }
.rev-row:has([aria-current]) .edit-tick { visibility: visible; }
.rev-row-n { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rev-row-n .draft-mark { margin-right: 0.2rem; }
.rev-row-note { grid-column: 3; overflow-wrap: anywhere; }
.rev-row-when { grid-column: 3; color: var(--muted); font-size: 0.8125rem; }
.rev-row:not(:has(.rev-row-note)) .rev-row-when { grid-row: 1; align-self: baseline; }
.rev-from { position: relative; z-index: 1; color: inherit; text-decoration: underline; text-underline-offset: 0.15em; }
/* An older revision is read only: the text's tools are greyed. */
.edit-bar.older .edit-text-tools { display: contents; }
.edit-bar .editor-status .draft-mark { margin-right: 0.375rem; }
.editor-status[data-problem] { color: var(--warn); }
/* The way to sign in again, after the words that say the session ended. */
.editor-signin { color: inherit; font-weight: 500; text-decoration: underline; text-underline-offset: 0.15em; }
.action.edit-publish { gap: 0.375rem; min-height: 2rem; height: 2rem; padding: 0 0.75rem; cursor: pointer; }
.edit-bar .editor-save { min-height: 2rem; height: 2rem; padding: 0 0.75rem; cursor: pointer; }
.edit-publish-glyph { width: 1em; height: 1em; flex-shrink: 0; }
/* Published, the globe and the word give way to a tick a moment, in the
 * middle of the button, as wide as it was, before the faded Publish comes
 * back. */
.action.edit-publish { position: relative; }
.edit-published-glyph { display: none; }
.edit-publish[data-done] > :not(.edit-published-glyph) { visibility: hidden; }
.edit-publish[data-done] .edit-published-glyph { display: block; position: absolute; inset: 0; width: 1.125em; height: 1.125em; margin: auto; animation: edit-done 0.3s ease-out; }
.action.edit-publish[data-done] { opacity: 1; }
/* While a publish goes, from a moment on, Publish turns to a spinner, as
 * wide as it was, and then to its tick once the server has answered (Tejas,
 * 21 Oct 2026); without motion, it fades in and out. */
/* Out of sight but not of a screen reader's hearing: the button is still
 * Publish, busy (the review of fe09a41). */
/* Discard Draft's ↺ the same while a discard goes, from where the author is. */
.editor-discard { position: relative; }
:is(.edit-publish, .editor-discard)[data-busy] > * { opacity: 0; }
:is(.edit-publish, .editor-discard)[data-busy]::after { content: ""; position: absolute; inset: 0; width: 1em; height: 1em; margin: auto; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: edit-spin 0.8s linear infinite; }
@keyframes edit-spin { to { transform: rotate(1turn); } }
@keyframes edit-wait { to { opacity: 0.35; } }
@keyframes edit-done { from { transform: scale(0.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .edit-publish[data-done] .edit-published-glyph { animation: none; }
  :is(.edit-publish, .editor-discard)[data-busy]::after { animation: edit-wait 0.8s ease-in-out infinite alternate; }
}
/* With nothing to publish, Publish keeps its colour, faded. */
.action.edit-publish[disabled] { opacity: 0.5; cursor: default; }
/* Its popovers, the browser's own: under the button that opens them, as
 * the script places them, scrolling within the room above a phone's
 * keyboard; without it, under the bar at its far end, where Discard and
 * Publish ask their questions. */
.edit-pop {
  position: fixed;
  inset: calc(3.5rem + var(--edit-bar) + 0.25rem) 1rem auto auto;
  margin: 0;
  padding: 0.375rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--popover);
  color: var(--foreground);
  box-shadow: var(--popover-shadow);
  font-size: 0.875rem;
  line-height: 1.25rem;
  overflow-y: auto;
}
.edit-insert-menu:popover-open { display: grid; grid-template-columns: 1fr 1fr; gap: 0.125rem; width: 20rem; }
.edit-insert-menu button { display: flex; align-items: center; gap: 0.625rem; padding: 0.4375rem 0.625rem; border: 0; border-radius: 0.375rem; background: none; font: inherit; color: var(--foreground); text-align: left; cursor: pointer; }
.edit-insert-menu svg { flex-shrink: 0; width: 1.125rem; height: 1.125rem; color: var(--muted); }
/* The prose the rich editor makes, its text's tools and Return, Insert
 * offers only while the block editors have the page, and on an empty page
 * (blank, which edit.js sets), where there is no text to make it from. */
.edit-bar.rich:not(.blank) .edit-insert-menu [data-prose] { display: none; }
/* The styles' menu, each style in its own look, the one the caret's blocks
 * have ticked, and its key at the end, as a word processor's. */
.edit-style-menu:popover-open { display: grid; gap: 0.125rem; width: 17rem; }
.edit-style-menu button { display: grid; grid-template-columns: 1rem 1fr auto; align-items: center; gap: 0.5rem; padding: 0.4375rem 0.625rem 0.4375rem 0.5rem; border: 0; border-radius: 0.375rem; background: none; font: inherit; color: var(--foreground); text-align: left; cursor: pointer; }
.edit-style-menu button[disabled] { opacity: 0.35; cursor: default; }
.edit-style-menu [aria-checked="true"] { background: var(--hover); }
.edit-tick { visibility: hidden; color: var(--accent); }
.edit-style-menu [aria-checked="true"] .edit-tick { visibility: visible; }
.edit-style-menu kbd { font: inherit; font-size: 0.75rem; color: var(--muted); }
@media (pointer: coarse) {
  .edit-style-menu kbd { display: none; }
}
.edit-style-heading { font-size: 1.125rem; line-height: 1.5rem; font-weight: 600; }
.edit-style-subheading { font-weight: 600; }
.edit-style-mark { display: inline-block; min-width: 1.375em; color: var(--muted); }
/* More Formatting's menu, each mark in its own look, ticked where the words
 * selected have it. The style's menu holds them too, under a rule, on a
 * phone, whose bar has no room for underline and the More menu (below). */
.edit-more-menu:popover-open { width: 15rem; }
.edit-style-menu .edit-phone-mark { display: none; }
.edit-style-menu [role="separator"] { height: 1px; margin: 0.25rem 0.5rem; background: var(--border); }
/* The link's question: its address, why one is refused, and its answers. */
.edit-link-pop:popover-open { display: grid; gap: 0.625rem; width: 22rem; padding: 0.625rem; }
.edit-link-pop .editor-note { width: 100%; }
.edit-link-problem { color: var(--warn); }
.edit-pop-actions .edit-unlink { margin-right: auto; }
.edit-confirm:popover-open, .edit-note:popover-open { display: grid; gap: 0.625rem; padding: 0.625rem; }
.edit-confirm { width: 17rem; }
.edit-note { width: 20rem; }
.edit-note .editor-note { width: 100%; }
.edit-pop-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }
.edit-pop-actions .action { min-height: 2.25rem; padding: 0 0.875rem; cursor: pointer; }
.action.edit-danger { border-color: var(--tone-rose); background: var(--tone-rose); color: #fff; }
/* On the owner's page the bar is out of sight while they read it, until
 * they click into the page (idle, which the page's script sets). */
.edit-bar.idle { display: none; }
/* While the page is edited the bar takes the header's place (live, which
 * the page's lines set, decision 32): drawn over it, as high, its border
 * too, the header kept where it is but unseen, so that nothing below moves
 * as one gives way to the other; read, the bar out of sight (idle), the
 * header is back. A browser without :has() shows both, the bar on the layer
 * above. Where the editor's script fails neither gives way: the bar sticks
 * under the header, both in sight. */
.edit-bar.live { top: 0; z-index: 21; margin-top: calc(-3.5rem - 1px); }
.edit-bar.live .edit-bar-inner { height: 3.5rem; }
body:has(.edit-bar.live:not(.idle)) .site-header { visibility: hidden; }
body:has(.edit-bar:not(.idle):not(.live)) .shell-sticky { top: calc(3.5rem + var(--edit-bar)); max-height: calc(100vh - 3.5rem - var(--edit-bar)); }
body:has(.edit-bar:not(.idle):not(.live)) .prose :where(h2, h3, h4, [data-footnote-ref], .footnotes li) { scroll-margin-top: calc(5rem + var(--edit-bar)); }
/* The sign-in page. */
.signin p:not(.actions) { margin-top: 1rem; max-width: 36rem; }
.signin-message { padding: 0.75rem 1rem; border: 1px solid var(--border); border-radius: 0.5rem; color: var(--warn); }
.signin form { margin-top: 1.75rem; }

/* Search */
.search { position: relative; width: 100%; }
.search-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.25rem;
  padding-inline: 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--sidebar);
}
.search-field:focus-within { border-color: var(--accent); }
.search-icon { flex-shrink: 0; width: 1rem; height: 1rem; color: var(--muted); }
.search-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  font-size: 0.875rem;
  line-height: 1.25rem;
  outline-style: none;
}
.search-input::placeholder { color: var(--muted); }
/* Chromium sets room aside in a search field for its clear button even while
 * the field is empty and the button hidden, which cut the end off the
 * placeholder on a phone. Empty, the field keeps no such room; the button
 * comes back with the first letter typed. */
.search-input:placeholder-shown::-webkit-search-cancel-button { display: none; }
.search-scope {
  flex-shrink: 0;
  padding: 0.125rem 0.375rem;
  border: 1px solid var(--border);
  border-radius: 0.25rem;
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted);
}
.search-kbd {
  display: none;
  flex-shrink: 0;
  padding-inline: 0.25rem;
  border: 1px solid var(--border);
  border-radius: 0.25rem;
  font-size: 10px;
  color: var(--muted);
}
.search-panel {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 30;
  max-height: 70vh;
  margin-top: 0.25rem;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--background);
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 10%), 0 4px 6px -4px rgb(0 0 0 / 10%);
}
.search-message { padding: 0.75rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }
.search-message button { text-decoration-line: underline; }
.search-result { padding: 0.5rem 0.75rem; }
.search-result[aria-selected="true"] { background: var(--accent-low); }
.search-hit { display: block; }
.search-hit-head { display: flex; align-items: baseline; gap: 0.5rem; }
.search-hit-title { font-size: 0.875rem; line-height: 1.25rem; font-weight: 500; }
.search-hit-meta { font-size: 0.75rem; line-height: 1rem; color: var(--muted); }
.search-hit-excerpt {
  display: -webkit-box;
  margin-top: 0.125rem;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted);
}
.search-chips { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.25rem; }
.search-chip {
  padding: 0.125rem 0.375rem;
  border-radius: 0.25rem;
  background: var(--code);
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted);
}

/* Pages outside projects */
/* The column is the project pages' column, so at wide widths the page sits
 * under the search exactly as an artifact does. */
.page { flex: 1; width: 100%; max-width: calc(var(--column-width) + 3rem); margin-inline: auto; padding: 3rem 1.5rem; }
.home-blurb { max-width: 42rem; margin-top: 0.5rem; font-size: 1.125rem; line-height: 1.75rem; color: var(--muted); }
.home-section { margin-top: 3rem; }
.project-cards { display: grid; gap: 1rem; margin-top: 1rem; }
.project-card {
  display: block;
  height: 100%;
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 0.15s;
}
.project-card h3 { display: flex; gap: 0.5rem; font-size: 1.125rem; line-height: 1.75rem; font-weight: 600; }
.project-card h3 .icon { width: 1.5em; height: 1.5em; } /* a size and a half, as the header's */
.project-card-summary { margin-top: 0.25rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }
.project-card-meta { margin-top: 0.75rem; font-size: 0.75rem; line-height: 1rem; color: var(--muted); }
.topic-list { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.5rem; }
.topic-list a {
  display: inline-block;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 9999px;
  font-size: 0.875rem;
  line-height: 1.25rem;
}
.topic-list span { color: var(--muted); }
.topic-title { margin-top: 0.25rem; }
.topic-section { margin-top: 2rem; }
.topic-projects { margin-top: 0.5rem; }
.topic-projects > :not(:last-child) { margin-bottom: 0.5rem; }
.topic-projects > li { display: flex; gap: 0.5rem; }
.topic-projects a { font-weight: 500; }
.topic-projects span { font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }
.not-found {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  width: 100%;
  max-width: 48rem;
  margin-inline: auto;
  padding: 6rem 1.5rem;
}
.not-found h1 { margin-top: 0.25rem; font-size: 1.5rem; line-height: 2rem; font-weight: 600; }
.not-found a { margin-top: 1.5rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--accent); }

/* Artifact lists */
.artifact-list > :not(:last-child) { border-bottom: 1px solid var(--border); }
.artifact-list > li { padding-block: 0.75rem; }
/* An artifact's icon takes a column of its own; its title, date and excerpt
 * keep to the next, so a title that wraps never runs under the icon. */
.artifact-list > li.has-icon { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 0.5rem; }
.artifact-list > li.has-icon > :not(.icon) { grid-column: 2; }
.artifact-list-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
.artifact-list-head a { font-weight: 500; }
.artifact-list-head span { font-size: 0.75rem; line-height: 1rem; color: var(--muted); }
.artifact-list-excerpt { margin-top: 0.25rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }

/* The project frame: header, sidebar, content column, table of contents.
 * The sidebar keeps to the screen's left edge at every width, as on
 * pluto-lang.dev, and the header spans the screen with it. */
.shell { display: flex; flex: 1; width: 100%; }
.shell-sidebar {
  display: none;
  flex-shrink: 0;
  width: var(--sidebar-width);
  border-right: 1px solid var(--border);
  background: var(--sidebar);
}
.shell-sticky {
  position: sticky;
  top: 3.5rem;
  max-height: calc(100vh - 3.5rem);
  overflow-y: auto;
  padding: 1rem 0.75rem;
}
.shell-main { flex: 1; min-width: 0; padding: 2rem var(--content-pad); }
/* A failed save's line lies under the bar, over the page's top: while it is
 * said, the top keeps clear of it (--problem, its height, edit.js), so that
 * what is there, a title and its icon, whose picker puts a refused value
 * right, stays in reach (a review of the logo). */
.shell-main { padding-top: max(2rem, calc(var(--problem, 0px) + 0.75rem)); }
.shell-menu {
  margin-bottom: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--sidebar);
}
/* Where the sidebar folds into the menu, the header's breadcrumb comes with
 * it. The Lab's name leads to the Lab, as in the header; the project, where
 * the reader already is, opens the project's navigation once the script
 * after the menu has made it a button (parts.html); until then, and without
 * a script, the navigation shows open. The Lab's name takes at most 45% of
 * the bar, so long names both give way and "Menu" keeps its place. Their
 * pictures are set in the line as in the header, a size up from the words,
 * overhanging the line rather than deepening it. */
.shell-menu-bar {
  display: flex;
  align-items: center;
  padding: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
}
.shell-menu-lab, .shell-menu-toggle { padding: 0.25rem 0.5rem; border-radius: 0.375rem; }
.shell-menu-lab, .shell-menu-where { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shell-menu-lab { flex: 0 1 auto; max-width: 45%; }
:is(.shell-menu-lab, .shell-menu-where) :is(.avatar, .icon, .mark) {
  display: inline-block;
  width: 1.5em;
  height: 1.5em;
  margin-block: -0.125em;
  margin-right: 0.4em;
  vertical-align: middle;
  vertical-align: calc(0.5cap - 0.625em);
}
.shell-menu-sep { flex-shrink: 0; color: var(--muted); }
.shell-menu-toggle {
  display: flex;
  flex: 1 1 0;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-width: 0;
  text-align: left;
}
button.shell-menu-toggle { cursor: pointer; }
.shell-menu-label { display: flex; flex-shrink: 0; align-items: center; gap: 0.25rem; color: var(--muted); }
.shell-menu-chevron {
  width: 0.875rem;
  height: 0.875rem;
  transition-property: rotate;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 0.15s;
}
.shell-menu[data-open] .shell-menu-chevron { rotate: 90deg; }
.shell-menu-body { padding: 0.75rem; border-top: 1px solid var(--border); }
.shell-menu[data-ready]:not([data-open]) .shell-menu-body { display: none; }
/* Beside the sidebar alone the column keeps to its left edge, so the rail's
 * arrival takes width off its right and the title does not move; with the
 * rail, it centres between the two. Pages without a table of contents keep
 * the rail's place empty, so the column holds still from page to page. */
.shell-content { max-width: var(--column-width); margin-inline: 0 auto; }
.shell-toc { display: none; flex-shrink: 0; width: var(--rail-width); }
.shell-toc-sticky { padding: 2rem 1rem; }

.sidebar { font-size: 0.875rem; line-height: 1.25rem; }
.sidebar-landing { display: flex; align-items: center; gap: 0.5em; padding: 0.375rem 0.5rem; border-radius: 0.375rem; font-weight: 600; }
.sidebar-landing[aria-current="page"] { background: var(--accent-low); color: var(--accent-high); }
.sidebar-section { margin-top: 1.25rem; }
.sidebar-section-head { display: flex; align-items: baseline; justify-content: space-between; padding-inline: 0.5rem; }
.sidebar-section-head :is(a, span) {
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  color: var(--muted);
}
.sidebar-items { margin-top: 0.25rem; }
.sidebar-items > :not(:last-child) { margin-bottom: 0.125rem; }
.sidebar-link {
  display: flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.25rem 0.5rem;
  border-radius: 0.375rem;
  line-height: 1.375;
  color: color-mix(in oklab, var(--foreground) 80%, transparent);
}
.sidebar-link[aria-current="page"] { background: var(--accent-low); font-weight: 500; color: var(--accent-high); }
.sidebar-group > summary {
  display: flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.25rem 0.5rem;
  border-radius: 0.375rem;
  list-style: none;
  cursor: pointer;
  font-weight: 500;
}
.sidebar-group > summary > a[aria-current="page"] { color: var(--accent-high); }
.sidebar-chevron {
  flex-shrink: 0;
  margin-left: auto;
  width: 0.875rem;
  height: 0.875rem;
  color: var(--muted);
  transition-property: transform, translate, scale, rotate;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 0.15s;
}
.sidebar-group[open] > summary > .sidebar-chevron { rotate: 90deg; }
.sidebar-group > ul { margin-left: 0.5rem; padding-left: 0.5rem; border-left: 1px solid var(--border); }
/* The owner's ● before a page with unpublished changes, as the Pluto
 * playground marks an edited example, in the accent, a space from the name.
 * At the text's own size the system font's ● is a disc as tall as the
 * capitals, louder than anything else in the sidebar; at three quarters it
 * sits on the middle of the lowercase letters. Readers are drawn none
 * (parts.html). */
.draft-mark { flex-shrink: 0; font-size: 0.75em; color: var(--accent); }
:is(.sidebar-link, .sidebar-landing) > .draft-mark { margin-right: -0.2em; }
.sidebar-group > summary > a > .draft-mark { margin-right: 0.3em; }

.toc-list { margin-top: 0.5rem; border-left: 1px solid var(--border); }
.toc-link {
  display: block;
  padding: 0.25rem 0.5rem 0.25rem 0.75rem;
  border-left: 2px solid transparent;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted);
}
.toc-link.toc-sub { padding-left: 1.5rem; }

/* Project and artifact pages */
.project-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1rem;
  font-size: 0.75rem;
  line-height: 1rem;
}
.chip-link { padding: 0.25rem 0.625rem; border: 1px solid var(--border); border-radius: 9999px; color: var(--muted); }

/* A project's Landing: the wordmark as the title of Paramsat's own project,
 * the actions under the summary (the first leads) and the features. */
.lockup-large { margin-bottom: 0; }
.lockup-large .wordmark { font-size: 2.25rem; line-height: 2.5rem; }
.lockup-large .lockup-reading { margin-left: 3.7125rem; font-size: 1rem; line-height: 1.5rem; } /* under "param" */
.landing-head .lead { margin-top: 1.25rem; font-size: 1.25rem; line-height: 1.875rem; }
.actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }
.action {
  display: inline-flex;
  align-items: center;
  min-height: 2.5rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
  transition-property: color, background-color, border-color;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 0.15s;
}
.action-lead { border-color: var(--accent-low); background: var(--accent-low); color: var(--accent-high); }
/* A picture, under a Landing's actions or the Lab's blurb, or a figure in
 * a page's body, one shape for all (internal/content, FigureHTML), with its
 * verse, caption and credit beneath. The verse is set in its own language
 * and the text's colour; the rest is muted, the credit a link to the
 * source. A picture taller than it is wide, a hanging scroll, is held to
 * the column's width in height instead and hung in the middle, its caption
 * with it. In a page's body the prose spaces it, as it spaces any figure. */
.figure { margin: 2rem 0 0; }
.figure img { display: block; width: 100%; height: auto; border: 1px solid var(--border); border-radius: 0.75rem; }
.figure.tall { display: flex; flex-direction: column; align-items: center; text-align: center; }
.figure.tall img { width: auto; max-width: 100%; max-height: var(--column-width); }
.figure figcaption { margin-top: 0.625rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--muted); }
.figure [lang="sa"] { color: var(--foreground); }
.figure-credit { font-style: italic; }
/* Blocks of the kinds a page's body may hold (internal/content, kinds.go):
 * cards, buttons and figures, drawn as a Landing's own are, the prose's
 * spacing for lists, paragraphs and links undone, and spaced as the
 * Landing spaces them. A figure sits across the column, or centred at a
 * smaller width, or beside the text, which flows around it as in a book;
 * on a phone it is across the column always. A heading starts below a
 * picture beside the text, as a book's new section does. */
.prose .kind { margin-top: 2rem; line-height: 1.5; }
.prose .kind-cards { margin-top: 2.5rem; }
.prose .kind-buttons { margin-top: 1.75rem; }
.prose > .kind:first-child { margin-top: 0; }
.block-body:has(> .kind-buttons:first-child) { margin-top: 1.75rem; }
.prose .features { margin: 0; padding: 0; list-style: none; }
.prose .feature { margin: 0; padding: 1.25rem; }
.prose .feature p { margin-bottom: 0; }
.prose .actions { margin: 0; }
.prose .actions .action { color: var(--foreground); font-weight: 500; text-decoration: none; }
.prose .actions .action-lead { color: var(--accent-high); }
.prose .figure :where(a) { color: inherit; font-weight: inherit; text-decoration: none; }
.figure-medium { max-width: 30rem; margin-inline: auto; }
.figure-small { max-width: 18rem; margin-inline: auto; }
.prose :where(h2, h3, h4) { clear: both; }
.block-body::after { content: ""; display: block; clear: both; }
/* Columns (decision 23): a row of columns, each narrow or wide, a wide one
 * twice a narrow one. The row stands side by side when its own box gives
 * every column at least --column-min, else its columns stack in reading
 * order, as on a phone, in whatever box holds it. The server writes on the
 * row how many of its narrowest columns it is worth (--row-share) and how
 * many gaps it has (--row-gaps), and one rule does the rest: a column's
 * basis is far below nothing while the row has room, and far wider than the
 * row once it has not (Heydon Pickering's Switcher). Stacked, columns stand
 * as far apart as a page's pictures and cards. Inside a column the blocks
 * keep the prose's spacing, less at its ends, a block's own first or last
 * part too, as a picture in its box; a picture beside the text there takes
 * the column's width. */
.columns { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 2rem var(--column-gap); }
.columns > .column { flex-grow: 1; flex-basis: calc((var(--column-min) * var(--row-share) + var(--column-gap) * var(--row-gaps) - 100%) * 999); min-width: 0; }
.columns > .column.wide { flex-grow: 2; }
.prose .columns { margin-top: 2rem; margin-bottom: 2rem; }
.prose .column > :first-child, .prose .column > :first-child > :first-child { margin-top: 0; }
.prose .column > :last-child, .prose .column > :last-child > :last-child { margin-bottom: 0; }
.columns-problem > .kind-problem { margin-bottom: 1rem; }
.kind-problem { padding: 0.75rem 1rem; border: 1px dashed var(--warn); border-radius: 0.5rem; font-size: 0.875rem; color: var(--warn); }
/* The owner's editor for an open part (edit.js): the part as its Markdown
 * or as a small form, its tools in the bar; in a row of cards or of buttons
 * it takes the place of the one it edits. */
.block-panel { margin-block: 1rem; line-height: 1.5; }
.features > .block-panel, .actions > .block-panel { margin: 0; }
.actions > .block-panel { flex-basis: 100%; }
/* What a card or a button has besides the bar's tools, at its form's foot. */
.item-tools { display: flex; flex-wrap: wrap; gap: 0.375rem; }
.item-tools button { padding: 0.25rem 0.625rem; border: 1px solid var(--border); border-radius: 0.375rem; background: var(--background); font-size: 0.8125rem; line-height: 1.25rem; color: var(--foreground); cursor: pointer; }
.item-form { display: grid; gap: 0.625rem; padding: 0.875rem 1rem; border: 1px solid var(--accent); border-radius: 0.75rem; background: var(--code); }
.item-form label { display: grid; gap: 0.25rem; font-size: 0.8125rem; line-height: 1.25rem; color: var(--muted); }
.item-form :is(input, select, textarea) { width: 100%; padding: 0.375rem 0.5rem; border: 1px solid var(--border); border-radius: 0.375rem; background: var(--background); font-size: 0.875rem; line-height: 1.375rem; color: var(--foreground); }
.item-form textarea { resize: vertical; }
/* Cards go two to a row while each keeps --column-min, else one: by the
 * width of their own box, so they go one to a row in a column of a row or
 * beside a picture as on a phone. */
.features { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, max(var(--column-min), (100% - 1rem) / 2)), 1fr)); gap: 1rem; margin-top: 2.5rem; }
.feature { padding: 1.25rem; border: 1px solid var(--border); border-radius: 0.75rem; }
.feature-title { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; }
.feature-title .icon { width: 1.375rem; height: 1.375rem; }
.feature p { margin-top: 0.375rem; font-size: 0.875rem; line-height: 1.375rem; color: var(--muted); }
.chip-topic { padding: 0.25rem 0.625rem; border-radius: 9999px; background: var(--code); color: var(--muted); }
.block-body { margin-top: 2rem; }
.project-section { margin-top: 3rem; }
.project-section-head { display: flex; align-items: baseline; justify-content: space-between; }
.project-section-head a { font-size: 0.75rem; line-height: 1rem; color: var(--muted); }
.section-list { margin-top: 1.5rem; }

.artifact-kind {
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  color: var(--muted);
}
.artifact-title { margin-top: 0.25rem; }
/* Two columns, so a title that wraps never runs under its icon. The pictures
 * scale with the title as the header's do with its names: the owner's a size
 * and a half, an icon a size and a quarter, hanging a little below the
 * baseline, which the half rem before the lead gives room for. */
.page-title.has-icon { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 0.4em; }
.page-title.has-icon .icon { width: 1.25em; height: 1.25em; }
.page-title.has-icon .avatar { width: 1.5em; height: 1.5em; }
/* The picture hangs below the title's baseline, so the line under a titled
 * picture stands a rem off rather than the half rem it has elsewhere. */
.page-title.has-icon ~ :is(.lead, .home-blurb) { margin-top: 1rem; }
/* A Landing's wordmark (decision 37): its name as a picture in the title,
 * in place of the name's words, the icon, the project's mark, kept beside it
 * at the title's size. The picture keeps the size its file gives it, up to
 * 6rem tall and the column's width, its proportions kept as it narrows, and
 * the one the page's theme takes is shown, Auto or chosen, as the site's
 * colours switch. A picture has no line of words for the icon to stand on:
 * the icon stands at its top, and a wordmark drawn as Paramsat's is, its
 * name's middle as far down as the icon's, puts the two level. The name stays
 * in words, unseen but by a screen reader, and in its place where the
 * picture does not load (wordmark-failed, base.html); its owner sees it under
 * the picture, small, to change it, and opens the wordmark's picker from the
 * picture, or from Insert's Picture with the caret in the name (edit.js). */
/* The name's part of the title holds the picture over the name, the name
 * across the column, as a title's words are, so that a click beside them
 * puts the caret at their end. */
.page-name { display: block; position: relative; min-width: 0; }
.page-name > :is([data-field="title"], .page-words) { display: block; }
.page-wordmark { display: block; }
.page-wordmark[hidden], .page-wordmark .wordmark-dark, .wordmark-failed .page-wordmark { display: none; }
.page-wordmark img { display: block; width: auto; max-width: 100%; height: auto; max-height: 6rem; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .page-wordmark:has(.wordmark-dark) .wordmark-light { display: none; }
  :root:not([data-theme="light"]) .page-wordmark .wordmark-dark { display: block; }
}
:root[data-theme="dark"] .page-wordmark:has(.wordmark-dark) .wordmark-light { display: none; }
:root[data-theme="dark"] .page-wordmark .wordmark-dark { display: block; }
.page-title.has-icon.has-wordmark:not(.wordmark-failed) { align-items: start; }
.page-title.has-icon.has-wordmark:not(.wordmark-failed) > :is(svg.icon, .page-icon) { margin-top: 0; translate: none; }
.page-title.has-wordmark:not(.wordmark-failed) ~ :is(.lead, .home-blurb) { margin-top: 1.25rem; }
/* Under a picture the name is unseen, the page as readers see it: theirs
 * for a screen reader, as .sr-only has it; its owner's among the picture's
 * details (edit.js, decision 38), where it had shown small under it. */
.page-title.has-wordmark:not(.wordmark-failed) .page-name > .page-words { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.page-title.has-wordmark:not(.wordmark-failed) .page-name > [data-field="title"] { display: none; }
button.page-wordmark { padding: 0; border: 0; border-radius: 0.375rem; background: none; cursor: pointer; }
button.page-wordmark:focus-visible { box-shadow: 0 0 0 0.25rem var(--hover); outline: none; }
/* Chosen by a click (edit.js, decision 38), the picture is ringed, and a ×
 * on its corner removes it, placed from the title's header, beside the
 * heading rather than in it, whose words name the project alone. */
header:has(> .page-title[data-wordmark]) { position: relative; }
.page-wordmark.chosen img { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 0.25rem; }
.part-remove { position: absolute; z-index: 2; display: grid; place-items: center; width: 2rem; height: 2rem; padding: 0; border: 1px solid var(--border); border-radius: 50%; background: var(--popover); box-shadow: var(--popover-shadow); color: var(--foreground); font: inherit; font-size: 0.9375rem; font-weight: 400; line-height: 1; letter-spacing: normal; cursor: pointer; }
.part-remove[hidden] { display: none; }
/* A picture in the text, for its owner (edit.js, decision 38): chosen by a
 * click, ringed as the title's is, a × on its corner, placed from its box;
 * reached by the keyboard, its block selected in the rich editor, a halo as
 * a focused button has, in place of the editor's own ring. The words under
 * it, its verse, caption and credit, are typed where they are drawn: an
 * empty one is drawn only while it is chosen, showing faintly what it is
 * for, and with none at all their line takes no room, under the picture in
 * the gap before the next block, so that choosing it moves nothing. Where
 * its picture did not load, a line says so in its place, with Replace. */
.kind[data-dressed] .figure img { cursor: pointer; -webkit-touch-callout: none; }
.ProseMirror .rich-opaque.ProseMirror-selectednode:is(.kind-figure, :has(> .kind-figure)) { outline: none; }
.ProseMirror-focused .ProseMirror-selectednode:is(.kind-figure, :has(> .kind-figure)) .figure img, .kind[data-dressed]:focus-visible .figure img { box-shadow: 0 0 0 0.25rem var(--hover); }
.kind[data-dressed]:focus-visible { outline: none; }
.kind-figure.chosen, .kind-figure.chosen .figure { position: relative; }
.kind-figure.chosen .figure img, .kind-figure.chosen > .kind-problem, .kind-figure.chosen .figure-notice, .kind-figure.chosen .figure-unset-frame { outline: 2px solid var(--accent); outline-offset: 3px; box-shadow: none; }
/* A picture whose address is not set yet, as one just added is: a frame in
 * its place saying so, its owner's alone, as a page holding one does not
 * load for readers. */
.figure-unset-frame { display: flex; align-items: center; justify-content: center; min-height: 10rem; border: 1px dashed var(--border); border-radius: 0.75rem; color: var(--muted); font-size: 0.875rem; line-height: 1.25rem; cursor: pointer; }
.caption-fields [data-caption] { outline: none; cursor: text; }
.caption-fields .figure-credit:hover { color: inherit; text-decoration-line: none; }
.caption-fields [data-caption]:empty { display: none; }
.kind-figure.chosen .caption-fields [data-caption]:empty { display: inline; }
.caption-fields [data-caption]:empty::before { content: attr(data-placeholder); color: color-mix(in oklab, currentcolor 50%, transparent); pointer-events: none; }
.caption-fields [data-caption="verse"]:empty + .caption-sep, .caption-fields .caption-sep:has(+ [data-caption="credit"]:empty) { display: none; }
.kind-figure.chosen .caption-fields .caption-sep { display: inline; }
.caption-fields:not(:has([data-caption]:not(:empty))) { display: none; }
.kind-figure.chosen .caption-fields:not(:has([data-caption]:not(:empty))) { display: block; position: absolute; inset: 100% 0 auto; }
.figure-notice { display: none; }
.kind-figure.picture-failed .figure img { display: none; }
.kind-figure.picture-failed .figure-notice { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.25rem 0.5rem; margin: 0; padding: 1.5rem 1rem; border: 1px dashed var(--border); border-radius: 0.75rem; font-size: 0.875rem; line-height: 1.25rem; color: var(--warn); }
.figure-replace { padding: 0.125rem 0.625rem; border: 1px solid var(--border); border-radius: 0.375rem; background: var(--background); color: var(--foreground); font: inherit; cursor: pointer; }
/* Where the picture did not load, its owner is told so, under the name in
 * words, and offered to replace it. */
.wordmark-notice { display: none; }
.page-title[data-wordmark].wordmark-failed .wordmark-notice { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; margin-top: 0.375rem; font-size: 0.875rem; line-height: 1.25rem; font-weight: 400; letter-spacing: normal; color: var(--warn); }
.page-title[data-wordmark-fixed][data-wordmark-dark-fixed] .wordmark-replace { display: none; }
.wordmark-replace { padding: 0.125rem 0.625rem; border: 1px solid var(--border); border-radius: 0.375rem; background: var(--background); color: var(--foreground); font: inherit; cursor: pointer; }
.artifact-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted);
}
.topic-tag { padding: 0.125rem 0.5rem; border-radius: 9999px; background: var(--code); }
.links {
  margin-top: 2.5rem;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--sidebar);
  font-size: 0.875rem;
  line-height: 1.25rem;
}
.links-list { margin-top: 0.25rem; }
.links-list > :not(:last-child) { margin-bottom: 0.25rem; }
.links-rel { color: var(--muted); }
.links-list a { color: var(--accent); }
.provenance { font-size: 0.75rem; line-height: 1rem; color: var(--muted); }
.links > div + .provenance { margin-top: 0.75rem; }
.pager {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  font-size: 0.875rem;
  line-height: 1.25rem;
}
.pager-next { text-align: right; }
.pager a { display: block; padding: 0.75rem 1rem; border: 1px solid var(--border); border-radius: 0.5rem; }
.pager-label { font-size: 0.75rem; line-height: 1rem; color: var(--muted); }
.pager-title { display: block; font-weight: 500; }

/* Hover states, for devices that hover */
@media (hover: hover) {
  .site-project:hover { color: var(--accent); }
  .site-nav a:hover { color: var(--foreground); }
  .shell-menu-lab:hover, button.shell-menu-toggle:hover { background: var(--hover); }
  .site-account:hover { color: var(--foreground); background: var(--hover); }
  .site-menu-theme button:hover, .site-menu-account :is(a, button):not(.action):hover { background: var(--hover); }
  .edit-link a:hover { color: var(--accent); }
  .block-body[data-editable] [data-lines]:hover { outline: 1px dashed var(--border); outline-offset: 0.375rem; border-radius: 0.125rem; }
  .item-tools button:hover { border-color: var(--accent); }
  .edit-bar-button:not([disabled]):hover, .edit-insert-menu button:hover, .edit-style-menu button:not([disabled]):hover, .rev-step[href]:hover, .rev-number:hover, .rev-row:hover, .pick-grid button:hover { background: var(--hover); }
  .page-icon:hover { box-shadow: 0 0 0 0.2em var(--hover); background: var(--hover); }
  button.page-wordmark:hover { box-shadow: 0 0 0 0.25rem var(--hover); }
  /* A picture in the text is chosen as the title's is, not opened as a
   * block: a halo as the pointer is over it, as the title's picture has. */
  .block-body[data-editable] .kind[data-dressed]:hover { outline: none; }
  .kind[data-dressed]:not(.chosen) .figure img:hover { box-shadow: 0 0 0 0.25rem var(--hover); }
  .part-remove:hover, .wordmark-replace:hover, .figure-replace:hover { background: var(--hover); }
  .rev-from:hover { color: var(--foreground); }
  .action.edit-danger:hover { border-color: var(--tone-rose); filter: brightness(1.08); }
  .search-scope:hover, .search-message button:hover, .search-chip:hover { color: var(--foreground); }
  .search-result:not([aria-selected="true"]):hover { background: var(--hover); }
  .project-card:hover { border-color: var(--accent); background: var(--hover); }
  .artifact-list-head a:hover, .topic-projects a:hover { color: var(--accent); }
  .topic-list a:hover { border-color: var(--accent); }
  .not-found a:hover, .links-list a:hover { text-decoration-line: underline; }
  .sidebar-landing:not([aria-current="page"]):hover { background: var(--hover); }
  .sidebar-section-head a:hover { color: var(--foreground); }
  .sidebar-link:not([aria-current="page"]):hover { background: var(--hover); color: var(--foreground); }
  .sidebar-group > summary:hover { background: var(--hover); }
  .toc-link:hover { border-color: var(--accent); color: var(--foreground); }
  .chip-link:hover { border-color: var(--accent); color: var(--foreground); }
  .chip-topic:hover, .topic-tag:hover, .project-section-head a:hover { color: var(--foreground); }
  .pager a:hover { border-color: var(--accent); }
  .action:hover { border-color: var(--accent); }
  .action.edit-publish[disabled]:hover { border-color: var(--accent-low); }
  .figure-credit:hover { color: var(--foreground); text-decoration-line: underline; }
  .pager a:hover .pager-title { color: var(--accent); }
}

/* Below 48rem the brand has no column of its own, so the header shows the
 * same brand on every page, the mark, and the search does not move as you
 * navigate; the breadcrumb goes down into the menu bar with the sidebar, and
 * the search's placeholder and scope button name the project. Below 40rem
 * Topics becomes its "#", the mark and the "#" at the two ends with the
 * search centred between them. Each query is the exact negation of its
 * min-width twin, so no width falls between them. */
@media not all and (min-width: 48rem) {
  .site-lab, .site-sep, .site-project { display: none; }
  .site-home { display: flex; margin: -0.5rem; padding: 0.5rem; }
  .site-brand .mark { margin-block: 0; }
}
/* Below 64rem the brand's column is too narrow for the owner's picture as
 * well as a project's name, and the names come first. */
@media not all and (min-width: 64rem) {
  .in-project .site-lab .avatar { display: none; }
}
@media not all and (min-width: 40rem) {
  /* The phone's margin is 16px, the reading sites' and the platforms' own:
   * from the screen's edge to the mark, to the account and to the text
   * below, so the text starts under the mark's left edge and ends under the
   * account's right. Inside the header the gaps beside the search are 12px,
   * which keeps the field its width. The mark and the account are each the
   * middle of a 43px target and as wide as the other, so the search sits in
   * the middle of the header. */
  :root { --content-pad: 1rem; }
  .site-header-inner { padding-inline: 1rem; gap: 0.75rem; }
  .edit-bar-tools { margin-left: -0.625rem; }
  .edit-bar-row { gap: 0.5rem; }
  .edit-bar-end { gap: 0.25rem; }
  .site-nav { gap: 0.75rem; }
  .page { padding-inline: 1rem; }
}
/* A row narrower than its tools in their words with the status beside
 * them, 48rem, holds them in the compact forms a phone's does (decision
 * 32): the status its mark alone, the revisions their number, the style its
 * ¶ and Publish its globe, their words left to screen readers. The row's
 * own width chooses, the sidebar beside it or not: at 800px, beside the
 * sidebar, the tools in their words ran Publish 15px past the screen (the
 * reviews of 0828337). A failed save's line (above) is placed by the bar
 * itself, since a query container places nothing it holds. */
@container (max-width: 48rem) {
  .editor-status:not([data-problem]) .editor-status-text, .edit-style-words, .edit-publish-word, .edit-part-details .edit-words { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .editor-status:not([data-problem]) { margin: 0; }
  .rev-step { display: none; }
  .edit-style { width: auto; gap: 0.25rem; }
  .edit-style-glyph { display: inline; }
  .edit-sep { display: none; }
}
@media not all and (min-width: 30rem) {
  /* A phone, at most 430px wide, holds every tool a wide screen's bar does,
   * the same bar (decision 32), in the compact forms (above) and drawn
   * smaller, so that they fit from 360px; narrower, the tools scroll
   * sideways beside the far end. An older revision's bar holds no text's
   * tools. */
  .edit-bar-row { gap: 0.25rem; }
  .edit-bar-tools, .edit-bar-end { gap: 0.125rem; }
  .edit-bar .edit-publish { padding: 0 0.5rem; }
  .edit-bar-icon { width: 1.75rem; }
  .edit-bar.older .edit-text-tools { display: none; }
}

/* A touch browser zooms the page into a field whose text is under 16px. And
 * iOS takes a tap on text for a click only where the text shows it can be
 * clicked, so the owner's blocks show it. */
@media (pointer: coarse) {
  .search-input, .editor-source, .editor-note, .block-source, .item-form :is(input, select, textarea) { font-size: 1rem; }
  .block-body[data-editable] [data-lines] { cursor: pointer; }
}

/* Wider screens: 640px, 768px, 1024px, 1280px */
@media (min-width: 40rem) {
  .prose .kind.layout-left { float: left; width: 45%; margin: 0.375rem 1.5rem 1rem 0; }
  .prose .kind.layout-right { float: right; width: 45%; margin: 0.375rem 0 1rem 1.5rem; }
  .prose .column .kind:is(.layout-left, .layout-right) { float: none; width: auto; margin: 2rem 0 0; }
  .prose .column > .kind:is(.layout-left, .layout-right):first-child { margin-top: 0; }
  :root { --content-pad: 2rem; }
  .search-kbd { display: inline; }
  .project-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 48rem) {
  /* The header mirrors the frame under it: the brand over the sidebar, the
   * search over the column (its left edge on the title's, its right on the
   * column's) and the account, when there is one, at the far end. Where the
   * column runs too close to the edge to leave the account room, the search
   * gives way, not the account. */
  .site-header-inner {
    display: grid;
    grid-template-columns:
      calc(var(--sidebar-width) + var(--content-pad))
      minmax(0, min(var(--column-width), 100% - var(--sidebar-width) - var(--rail-width) - 2 * var(--content-pad)))
      minmax(0, 1fr)
      auto;
    gap: 0;
    padding-inline: 0;
  }
  .site-brand { grid-column: 1; padding-inline: 1rem 0.5rem; }
  /* A rule sets the mark, the site's, apart from the breadcrumb, the Lab's;
   * on the site's own pages there is no breadcrumb, and the mark stands
   * alone. */
  .site-home { margin-right: 0.25rem; padding-right: 0.5rem; border-right: 1px solid var(--border); }
  .site-brand.alone .site-home { margin-right: 0; padding-right: 0; border-right: 0; }
  .site-search { grid-column: 2; }
  .site-nav { grid-column: 4; margin-left: 1rem; padding-right: 1rem; }
  .edit-bar-inner {
    display: grid;
    grid-template-columns:
      calc(var(--sidebar-width) + var(--content-pad))
      minmax(0, min(var(--column-width), 100% - var(--sidebar-width) - var(--rail-width) - 2 * var(--content-pad)))
      minmax(0, 1fr)
      auto;
    padding-inline: 0;
  }
  .edit-bar-row { grid-column: 2 / -1; padding-right: 1rem; }
  /* A failed save's words start at the column's left edge, as the tools do. */
  .edit-bar .editor-status[data-problem], .edit-bar .kept-line { padding-left: calc(var(--sidebar-width) + var(--content-pad)); }
  .shell-sidebar { display: block; }
  .shell-menu { display: none; }
}
@media (min-width: 64rem) {
  :root { --content-pad: 3rem; }
}
@media (min-width: 80rem) {
  :root { --rail-width: 16rem; }
  /* Past the frame's own width the column and its rail stay together,
   * centred in the room beside the sidebar: the margins take what is over.
   * The column's left edge is where the header's sum below puts the search. */
  .shell-main { flex: 0 1 calc(var(--column-width) + 2 * var(--content-pad)); margin-left: auto; }
  .shell-toc { margin-right: auto; }
  /* The column centres between the sidebar and the rail, and the search
   * with it. */
  .site-header-inner, .edit-bar-inner {
    grid-template-columns:
      calc(var(--sidebar-width) + var(--content-pad) + max(0rem, (100% - var(--sidebar-width) - var(--rail-width) - 2 * var(--content-pad) - var(--column-width)) / 2))
      minmax(0, min(var(--column-width), 100% - var(--sidebar-width) - var(--rail-width) - 2 * var(--content-pad)))
      minmax(0, 1fr)
      auto;
  }
  .edit-bar .editor-status[data-problem], .edit-bar .kept-line { padding-left: calc(var(--sidebar-width) + var(--content-pad) + max(0rem, (100% - var(--sidebar-width) - var(--rail-width) - 2 * var(--content-pad) - var(--column-width)) / 2)); }
  .shell-content { margin-inline: auto; }
  .shell-toc { display: block; }
}

/* Prose: the typography plugin's rules, closer to a textbook than marketing
 * copy. The :where() selectors keep its specificity, so the rules after them
 * settle the cascade the way they did. */
.prose { max-width: none; font-size: 1rem; line-height: 1.65; color: var(--foreground); }
.prose :where(p) { margin-top: 1.25em; margin-bottom: 1.25em; }
.prose :where(a) { color: var(--accent); font-weight: 500; text-decoration: underline; }
.prose :where(strong) { color: var(--foreground); font-weight: 600; }
.prose :where(a strong, blockquote strong, thead th strong) { color: inherit; }
.prose :where(ol) { margin-top: 1.25em; margin-bottom: 1.25em; padding-inline-start: 1.625em; list-style-type: decimal; }
.prose :where(ol[type="A"]) { list-style-type: upper-alpha; }
.prose :where(ol[type="a"]) { list-style-type: lower-alpha; }
.prose :where(ol[type="I"]) { list-style-type: upper-roman; }
.prose :where(ol[type="i"]) { list-style-type: lower-roman; }
.prose :where(ol[type="1"]) { list-style-type: decimal; }
.prose :where(ul) { margin-top: 1.25em; margin-bottom: 1.25em; padding-inline-start: 1.625em; list-style-type: disc; }
.prose :where(ol > li)::marker { color: var(--muted); font-weight: 400; }
.prose :where(ul > li)::marker { color: var(--muted); }
.prose :where(dt) { margin-top: 1.25em; color: var(--foreground); font-weight: 600; }
.prose :where(hr) { margin-top: 3em; margin-bottom: 3em; border-color: var(--border); border-top-width: 1px; }
.prose :where(blockquote) {
  margin-top: 1.6em;
  margin-bottom: 1.6em;
  padding-inline-start: 1em;
  border-inline-start-width: 0.25rem;
  border-inline-start-color: var(--border);
  color: var(--foreground);
  font-style: italic;
  font-weight: 500;
  quotes: "\201C" "\201D" "\2018" "\2019";
}
.prose :where(blockquote p:first-of-type)::before { content: open-quote; }
.prose :where(blockquote p:last-of-type)::after { content: close-quote; }
.prose :where(h1) { margin-top: 0; margin-bottom: 0.888889em; color: var(--foreground); font-size: 2.25em; font-weight: 800; line-height: 1.11111; }
.prose :where(h1 strong) { color: inherit; font-weight: 900; }
.prose :where(h2) { margin-top: 2em; margin-bottom: 1em; color: var(--foreground); font-size: 1.5em; font-weight: 700; line-height: 1.33333; }
.prose :where(h2 strong) { color: inherit; font-weight: 800; }
.prose :where(h3) { margin-top: 1.6em; margin-bottom: 0.6em; color: var(--foreground); font-size: 1.25em; font-weight: 600; line-height: 1.6; }
.prose :where(h3 strong) { color: inherit; font-weight: 700; }
.prose :where(h4) { margin-top: 1.5em; margin-bottom: 0.5em; color: var(--foreground); font-weight: 600; line-height: 1.5; }
.prose :where(h4 strong) { color: inherit; font-weight: 700; }
.prose :where(img, video) { margin-top: 2em; margin-bottom: 2em; }
.prose :where(picture) { display: block; margin-top: 2em; margin-bottom: 2em; }
.prose :where(kbd) {
  padding: 0.1875em 0.375em;
  border-radius: 0.3125rem;
  box-shadow: 0 0 0 1px var(--kbd-shadow), 0 3px 0 var(--kbd-shadow);
  color: var(--kbd);
  font-family: inherit;
  font-size: 0.875em;
  font-weight: 500;
}
.prose :where(code) { color: var(--foreground); font-size: 0.875em; font-weight: 600; }
.prose :where(a code, h1 code) { color: inherit; }
.prose :where(h2 code) { color: inherit; font-size: 0.875em; }
.prose :where(h3 code) { color: inherit; font-size: 0.9em; }
.prose :where(h4 code, blockquote code, thead th code) { color: inherit; }
.prose :where(pre) {
  margin-top: 1.71429em;
  margin-bottom: 1.71429em;
  padding: 0.857143em 1.14286em;
  overflow-x: auto;
  border-radius: 0.375rem;
  background-color: var(--code);
  color: var(--foreground);
  font-size: 0.875em;
  font-weight: 400;
  line-height: 1.71429;
}
.prose :where(pre code) {
  padding: 0;
  border-width: 0;
  border-radius: 0;
  background-color: transparent;
  color: inherit;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
}
.prose :where(table) { width: 100%; margin-top: 2em; margin-bottom: 2em; table-layout: auto; font-size: 0.875em; line-height: 1.71429; }
.prose :where(thead) { border-bottom-width: 1px; border-bottom-color: var(--border); }
.prose :where(thead th) {
  padding-inline-end: 0.571429em;
  padding-bottom: 0.571429em;
  padding-inline-start: 0.571429em;
  color: var(--foreground);
  font-weight: 600;
  vertical-align: bottom;
}
.prose :where(tbody tr) { border-bottom-width: 1px; border-bottom-color: var(--border); }
.prose :where(tbody tr:last-child) { border-bottom-width: 0; }
.prose :where(tbody td) { vertical-align: baseline; }
.prose :where(tfoot) { border-top-width: 1px; border-top-color: var(--border); }
.prose :where(tfoot td) { vertical-align: top; }
.prose :where(th, td) { text-align: start; }
.prose :where(figure > *) { margin-top: 0; margin-bottom: 0; }
.prose :where(figcaption) { margin-top: 0.857143em; color: var(--muted); font-size: 0.875em; line-height: 1.42857; }
.prose :where(picture > img) { margin-top: 0; margin-bottom: 0; }
.prose :where(li) { margin-top: 0.5em; margin-bottom: 0.5em; }
.prose :where(ol > li, ul > li) { padding-inline-start: 0.375em; }
.prose :where(.prose > ul > li p) { margin-top: 0.75em; margin-bottom: 0.75em; }
.prose :where(.prose > ul > li > p:first-child, .prose > ol > li > p:first-child) { margin-top: 1.25em; }
.prose :where(.prose > ul > li > p:last-child, .prose > ol > li > p:last-child) { margin-bottom: 1.25em; }
.prose :where(ul ul, ul ol, ol ul, ol ol) { margin-top: 0.75em; margin-bottom: 0.75em; }
.prose :where(dl) { margin-top: 1.25em; margin-bottom: 1.25em; }
.prose :where(dd) { margin-top: 0.5em; padding-inline-start: 1.625em; }
.prose :where(hr + *, h2 + *, h3 + *, h4 + *) { margin-top: 0; }
.prose :where(thead th:first-child) { padding-inline-start: 0; }
.prose :where(thead th:last-child) { padding-inline-end: 0; }
.prose :where(tbody td, tfoot td) { padding: 0.571429em; }
.prose :where(tbody td:first-child, tfoot td:first-child) { padding-inline-start: 0; }
.prose :where(tbody td:last-child, tfoot td:last-child) { padding-inline-end: 0; }
.prose :where(figure) { margin-top: 2em; margin-bottom: 2em; }
.prose :where(.prose > :first-child) { margin-top: 0; }
.prose :where(.prose > :last-child) { margin-bottom: 0; }

/* The Lab's own prose rules, which win over the defaults above. */
.prose h2 { margin-top: 2.25em; }
/* What a link in the page jumps to stops below the sticky header: headings,
 * and a footnote's note and the reference it links back to. */
.prose :where(h2, h3, h4, [data-footnote-ref], .footnotes li) { scroll-margin-top: 5rem; }
.prose pre { border: 1px solid var(--border); border-radius: 0.5rem; font-size: 0.875em; }
.prose :where(code):not(pre code) {
  padding: 0.1em 0.35em;
  border: 1px solid var(--border);
  border-radius: 0.3rem;
  background: var(--code);
  font-weight: 500;
}
.prose svg { max-width: 100%; height: auto; }
.table-wrap { overflow-x: auto; }

/* Code: chroma's class names, coloured as the GitHub themes colour them. A
 * block with a language is code-coloured even where nothing is highlighted. */
.prose pre.chroma { color: var(--code-text); }
.chroma :is(.c, .c1, .ch, .cm, .cp, .cpf, .cs) { color: var(--code-comment); }
.chroma :is(.k, .kd, .kn, .kp, .kr, .kt, .o, .ow) { color: var(--code-keyword); }
.chroma :is(.s, .s1, .s2, .sa, .sb, .sc, .sd, .dl, .sh, .sx, .sr, .ss, .l, .ld) { color: var(--code-string); }
.chroma :is(.se, .si, .m, .mb, .mf, .mh, .mi, .il, .mo, .kc, .no, .nb, .bp) { color: var(--code-constant); }
.chroma :is(.nf, .fm, .nc, .ne, .nd, .na) { color: var(--code-function); }
.chroma .nt { color: var(--code-tag); }
.chroma .gd { background: var(--code-deleted-bg); color: var(--code-deleted); }
.chroma .gi { background: var(--code-inserted-bg); color: var(--code-inserted); }
.chroma :is(.gh, .gu) { color: var(--code-constant); font-weight: 700; }
.chroma .ge { font-style: italic; }
.chroma .gs { font-weight: 700; }
.chroma .err { color: var(--code-deleted); }

/* The rich editor (rich.js, decision 26): the owner's page itself, typed
 * into, drawn exactly as readers see it. ProseMirror's own few rules,
 * which keep spaces as typed and the caret where it should be; a tight
 * list's items, which the editor holds as paragraphs, as close as the page
 * sets them; and the blocks it does not type into, drawn as the page draws
 * them, a click on one opening it in the block editors. While it holds the
 * page there is no part open, and so no part's tools in the bar. */
.ProseMirror { position: relative; outline: none; white-space: pre-wrap; white-space: break-spaces; word-wrap: break-word; font-variant-ligatures: none; font-feature-settings: "liga" 0; }
.ProseMirror li { position: relative; }
.ProseMirror .rich-opaque { white-space: normal; cursor: pointer; }
/* The words the link's question is for, while its field has the focus. */
.ProseMirror .rich-linking { background: var(--accent-low); }
.ProseMirror-hideselection *::selection { background: transparent; }
.ProseMirror-hideselection { caret-color: transparent; }
.ProseMirror-selectednode { outline: 2px solid var(--accent); outline-offset: 0.25rem; }
.ProseMirror-separator { display: inline !important; border: none !important; margin: 0 !important; }
.ProseMirror-gapcursor { display: none; position: absolute; pointer-events: none; }
.ProseMirror-gapcursor::after { content: ""; display: block; position: absolute; top: -2px; width: 20px; border-top: 1px solid var(--foreground); animation: rich-cursor 1.1s steps(2, start) infinite; }
.ProseMirror-focused .ProseMirror-gapcursor { display: block; }
@keyframes rich-cursor { to { visibility: hidden; } }
.prose :where([data-tight] > li > p) { margin-top: 0; margin-bottom: 0; }
/* An empty page, for its owner: Insert's + in the middle of the column, under
 * its one line, empty, adds a block, a text one too (Tejas, 20 Oct 2026). */
.page-add { display: flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; margin: 0.75rem auto 0; padding: 0; border: 1px solid var(--border); border-radius: 50%; background: none; color: var(--muted); font: inherit; font-size: 1.25rem; line-height: 1; cursor: pointer; }
.page-add:hover { background: var(--hover); color: var(--foreground); }
.page-add[hidden] { display: none; }
/* The page's own fields, its title and the line under it, typed where they
 * are drawn (edit.js): no box, as the text has none; empty, a field shows
 * faintly what it is for, the title the name the page shows without one,
 * and the line under it keeps its line before the script makes it a field.
 * An empty title shows that name as readers see it until then, and where
 * the script does not load (the review of b190305). */
[data-field][contenteditable] { outline: none; cursor: text; }
[data-field="title"]:empty::before, [data-field][contenteditable]:empty::before { content: attr(data-placeholder); }
[data-field][contenteditable]:empty::before { color: color-mix(in oklab, currentcolor 50%, transparent); pointer-events: none; }
.lead[data-field]:empty { min-height: 1.75rem; }
/* The page's icon, for its owner, a button the icon's size that opens the
 * icon picker (edit.js), ringed as the pointer is over it; a page with none
 * shows a faint ring in its place. */
.page-icon { display: inline-flex; align-items: center; justify-content: center; width: 1.25em; height: 1.25em; padding: 0; border: 0; border-radius: 0.3em; background: none; color: inherit; font: inherit; cursor: pointer; }
.page-icon:focus-visible, .page-icon[aria-expanded="true"] { box-shadow: 0 0 0 0.2em var(--hover); background: var(--hover); outline: none; }
.page-icon-none { border: 0.08em dashed var(--border); border-radius: 50%; box-sizing: border-box; }
/* The pickers (edit.js; Tejas, 22 Oct 2026): the page's icon, under it, its
 * tones as swatches over the set's icons and Starlight's, an emoji field and
 * Remove Icon at its foot; and the bar's emoji and characters, under its ☺,
 * by their groups. Each searches by name. A phone's bar, where every tool
 * fits from 360px, leaves out the ☺, its keyboard having emoji of its own,
 * and underline and More Formatting, whose marks the style's menu holds
 * there. */
.icon-pick:popover-open, .edit-emoji-pop:popover-open { display: grid; gap: 0.5rem; width: 22rem; padding: 0.625rem; }
.icon-pick-tones { display: flex; flex-wrap: wrap; gap: 0.375rem; padding: 0.125rem; }
.icon-pick-tones[hidden] { display: none; }
.icon-pick-tones button { width: 1.25rem; height: 1.25rem; padding: 0; border: 0; border-radius: 50%; background: currentcolor; cursor: pointer; }
.icon-pick-tones button[aria-pressed="true"] { box-shadow: 0 0 0 0.125rem var(--popover), 0 0 0 0.25rem currentcolor; }
.pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(2.25rem, 1fr)); gap: 0.125rem; max-height: 15rem; overflow-y: auto; }
.pick-grid button { display: flex; align-items: center; justify-content: center; height: 2.25rem; padding: 0; border: 0; border-radius: 0.375rem; background: none; color: var(--foreground); font: inherit; font-size: 1.25rem; cursor: pointer; }
.pick-grid button[aria-pressed="true"] { background: var(--hover); }
.pick-grid .icon { width: 1.25rem; height: 1.25rem; }
.pick-grid button[hidden], .pick-group[hidden] { display: none; }
.pick-group { grid-column: 1 / -1; padding: 0.375rem 0.25rem 0; color: var(--muted); font-size: 0.75rem; }
.icon-pick-foot { display: flex; align-items: center; gap: 0.5rem; }
.icon-pick-foot[hidden], .icon-pick [hidden] { display: none; }
.icon-pick-foot .editor-note { flex: 1 1 auto; }
.icon-pick-foot .action { min-height: 2.5rem; padding: 0 0.75rem; cursor: pointer; }
.pick-problem { margin: 0; color: var(--warn); font-size: 0.8125rem; }
@media not all and (min-width: 30rem) {
  .edit-emoji, .edit-bar-button[data-mark="u"], .edit-more { display: none; }
  .edit-style-menu button.edit-phone-mark { display: grid; }
  .edit-style-menu [role="separator"].edit-phone-mark { display: block; }
}
/* While the rich editor has the page the bar holds no part's tools but Done,
 * which ends the editing (decision 32). */
.edit-bar.rich .edit-part-tools [data-tool]:not([data-tool="done"], [data-tool="details"]) { display: none !important; }
