/* notabene renderer — readable theme, auto light/dark.

   THEMING CONTRACT: the `--nb-*` custom properties below are the PUBLIC theming
   surface — override them from a consumer theme (config `theme.css` / `theme.tokens`,
   loaded after this file). The un-prefixed variables are INTERNAL aliases consuming
   the contract; never target them in a theme — print.css deliberately overrides the
   internals to force a light palette on /print, so a theme can restyle the site
   without ever breaking the PDF export. Token list mirrored in lib/theme-tokens.mjs
   (validated config) and documented in the customize guide. */

/* Cascade layers: the renderer's own styles live in layers so a consumer theme
   (config `theme.css` / `theme.tokens`, UN-layered) always wins the cascade —
   whatever order Astro injects the bundled stylesheets in. nb-print > nb-base. */
@layer nb-base, nb-print;

@layer nb-base {


:root {
  /* Both schemes are first-class; the manual toggle forces one via [data-scheme]
     (set by the renderer's scheme toggle — themes must not touch this attribute).
     Every light-dark() below — the renderer's AND a theme's — flips with it. */
  color-scheme: light dark;

  /* ── public theming contract (--nb-*) — color tokens are light-dark() pairs ── */
  --nb-bg: light-dark(#ffffff, #0e1116);
  --nb-bg-soft: light-dark(#f6f7f9, #151a21);
  --nb-bg-elev: light-dark(#ffffff, #161b22);
  --nb-border: light-dark(#e4e7ec, #272e38);
  --nb-text: light-dark(#1c2024, #e7ebf0);
  --nb-text-soft: light-dark(#5b6470, #aab2bd);
  --nb-text-faint: light-dark(#8a929e, #768091);
  --nb-accent: light-dark(#2f6feb, #6ea0ff);
  --nb-accent-soft: light-dark(#e8f0ff, #182539);
  --nb-ref: light-dark(#2f6feb, #6ea0ff);
  --nb-work: light-dark(#b5651d, #e0a060);
  --nb-code-bg: light-dark(#0d1117, #0b0e13);
  --nb-topbar-h: 52px;
  --nb-sidebar-w: 290px;
  --nb-toc-w: 320px;
  /* Reading measure for the doc content. Responsive: ~50rem on laptops, growing to 80rem
     on large screens so wide monitors aren't mostly empty gutter — capped so prose lines
     stay readable. Tables/code/prose fill it; diagrams/images stay natural size, centered. */
  --nb-content-max: clamp(50rem, 62vw, 80rem);
  --nb-radius: 8px;
  --nb-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --nb-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* ── internal aliases (consume the contract; print.css overrides THESE) ── */
  --bg: var(--nb-bg);
  --bg-soft: var(--nb-bg-soft);
  --bg-elev: var(--nb-bg-elev);
  --border: var(--nb-border);
  --text: var(--nb-text);
  --text-soft: var(--nb-text-soft);
  --text-faint: var(--nb-text-faint);
  --accent: var(--nb-accent);
  --accent-soft: var(--nb-accent-soft);
  --ref: var(--nb-ref);
  --work: var(--nb-work);
  --code-bg: var(--nb-code-bg);
  --topbar-h: var(--nb-topbar-h);
  --sidebar-w: var(--nb-sidebar-w);
  --toc-w: var(--nb-toc-w);
  --content-max: var(--nb-content-max);
  --radius: var(--nb-radius);
  --mono: var(--nb-mono);
  --sans: var(--nb-sans);
}

/* Manual scheme forcing (the topbar toggle; persisted as localStorage "nb-scheme",
   applied pre-paint by DocLayout's inline head script). One property flips every
   light-dark() in the document, native form controls and scrollbars included. */
:root[data-scheme="light"] {
  color-scheme: light;
}
:root[data-scheme="dark"] {
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

/* The `hidden` attribute must always win over component `display` values
   (comment popovers, collapsed reply forms, etc.). */
[hidden] {
  display: none !important;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--topbar-h) + 1rem);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

/* ── Topbar ───────────────────────────────────────────────────────────── */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0 1rem;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
}
.brand {
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
}
.brand span {
  font-weight: 400;
  color: var(--text-faint);
}
.brand:hover {
  text-decoration: none;
}

.search {
  position: relative;
  flex: 1;
  max-width: 520px;
}
.topbar-link {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-soft);
  white-space: nowrap;
}
.topbar-link:hover {
  color: var(--accent);
  text-decoration: none;
}

/* ── Outbound nav links (config `nav`) ────────────────────────────────────
   `.nb-nav-link` (any mount point), `.nb-sidebar-links` (the sidebar block) and
   `.site-footer` are PUBLIC theming hooks — documented in the customize guide,
   stable across versions. Icons are monochrome `currentColor`, so they follow the
   link color and therefore the theme. */
.nb-nav-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.nb-nav-link svg {
  flex: none;
}
/* Topbar `iconOnly`: the icon IS the target — give it a hit area. */
.nb-nav-link--icon {
  padding: 4px;
  border-radius: 6px;
}
.nb-nav-link--icon:hover {
  background: var(--accent-soft);
}
.topbar-me {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--text-faint);
}
.topbar-me input {
  width: 8ch;
  padding: 2px 6px;
  font-size: 13px;
  color: var(--text);
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.topbar-me input:focus {
  width: 12ch;
  border-color: var(--accent);
  outline: none;
}
.search input {
  width: 100%;
  height: 34px;
  padding: 0 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-soft);
  color: var(--text);
  font-size: 14px;
}
.search input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg);
}
.search-results {
  position: absolute;
  top: 40px;
  left: 0;
  right: 0;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  padding: 4px;
}
.search-results a {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
  padding: 7px 9px;
  border-radius: 6px;
  color: var(--text);
}
.search-results a:hover {
  background: var(--accent-soft);
  text-decoration: none;
}
.r-space {
  font-size: 11px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 999px;
  color: #fff;
  flex-shrink: 0;
}
.r-space.reference {
  background: var(--ref);
}
.r-space.workbench {
  background: var(--work);
}
.r-empty {
  padding: 10px;
  color: var(--text-faint);
}
/* Pagefind excerpt (public builds) — full-width second line under the chip + title. */
.r-excerpt {
  flex-basis: 100%;
  font-size: 12px;
  line-height: 1.35;
  color: var(--text-faint);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.r-excerpt mark {
  background: var(--accent-soft);
  color: var(--text);
  border-radius: 3px;
  padding: 0 2px;
}

/* Flexible gap that separates the primary nav (Comments/Journal) from the ancillary
   Export + name group, pushing the latter to the far right of the topbar. */
.topbar-spacer {
  flex: 1 1 0;
}

/* ── Language switcher (i18n) ──────────────────────────────────────────── */
/* Drawer (compact): the languages stay a vertical list of pills. */
.lang-opt {
  padding: 2px 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-faint);
  border-radius: 6px;
}
.lang-opt:hover {
  color: var(--accent);
  text-decoration: none;
}
.lang-opt.active {
  color: var(--text);
  background: var(--bg-soft);
}
/* The two topbar dropdowns are <details>, and their <summary> is inline-flex — an
   INLINE-level box, so it sits on a line box whose strut adds descender space BELOW it.
   The <details> then measures taller than its summary and, centered as a flex item of
   .topbar, rides its icon 1-2px above every other util (which are plain flex items).
   Making the <details> itself a flex container removes the line box entirely. */
.lang-menu,
.export-menu {
  display: inline-flex;
  align-items: center;
}

/* Topbar: a dropdown next to Export (globe + current locale → pick a language). */
.lang-menu {
  position: relative;
}
.lang-menu > summary {
  cursor: pointer;
  list-style: none;
}
.lang-menu > summary::-webkit-details-marker {
  display: none;
}
.lang-summary {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 7px;
  border-radius: 6px;
  color: var(--text-faint);
  font-size: 12px;
  font-weight: 600;
}
.lang-summary:hover,
.lang-menu[open] .lang-summary {
  color: var(--text);
  background: var(--bg-soft);
}
.lang-summary svg {
  display: block;
}
.lang-menu-pop {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  min-width: 160px;
  display: flex;
  flex-direction: column;
  padding: 4px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
}
.lang-menu-pop a,
.lang-menu-pop button {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border: none;
  background: none;
  border-radius: 6px;
  color: var(--text);
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  text-align: left;
  cursor: pointer;
}
.lang-menu-pop a:hover,
.lang-menu-pop button:hover {
  background: var(--accent-soft);
  text-decoration: none;
}
.lang-menu-pop a.active,
.lang-menu-pop button.active {
  background: var(--bg-soft);
}
.lang-menu-pop .lang-code {
  min-width: 2.2ch;
  font-weight: 700;
  font-size: 11px;
  color: var(--text-faint);
}
.lang-menu-pop a.active .lang-code,
.lang-menu-pop button.active .lang-code {
  color: var(--accent);
}
/* Discreet notice when a page isn't available in the visitor's preferred language. */
.i18n-banner {
  margin: 0 0 18px;
  padding: 8px 12px;
  font-size: 13px;
  color: var(--text-faint);
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* ── Export PDF menu (topbar dropdown) ─────────────────────────────────── */
.export-menu {
  position: relative;
}
.export-menu > summary {
  cursor: pointer;
  list-style: none;
}
.export-menu > summary::-webkit-details-marker {
  display: none;
}
.export-summary {
  display: inline-flex;
  align-items: center;
  padding: 2px;
}
.export-summary svg {
  display: block;
}
.export-menu-pop {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  padding: 4px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
}
.export-menu-pop a {
  padding: 7px 10px;
  border-radius: 6px;
  color: var(--text);
  font-size: 13px;
  white-space: nowrap;
}
.export-menu-pop a:hover {
  background: var(--accent-soft);
  text-decoration: none;
}

/* ── Identity chip (opens the identity dialog) ─────────────────────────── */
button.topbar-me,
button.drawer-me {
  font-family: inherit;
  background: none;
  border: none;
  cursor: pointer;
}
button.topbar-me {
  padding: 4px 8px;
  border-radius: 6px;
  color: var(--text-faint);
}
button.topbar-me:hover {
  color: var(--text);
  background: var(--bg-soft);
}
.nb-identity-name {
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Identity dialog ───────────────────────────────────────────────────── */
.nb-dialog {
  width: calc(100vw - 2rem);
  max-width: 420px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-elev);
  color: var(--text);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
}
.nb-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}
.nb-identity-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
}
.nb-identity-form h2 {
  margin: 0;
  font-size: 1.2rem;
}
.nb-identity-lede {
  margin: 0;
  color: var(--text-soft);
  font-size: 0.9rem;
  line-height: 1.5;
}
.nb-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.nb-field-label {
  font-size: 0.85rem;
  font-weight: 600;
}
.nb-field-label em {
  font-weight: 400;
  font-style: normal;
  color: var(--text-faint);
}
.nb-field input {
  padding: 9px 11px;
  font-size: 0.95rem;
  color: var(--text);
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.nb-field input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg);
}
.nb-hint {
  color: var(--text-faint);
  font-size: 0.8rem;
}
.nb-identity-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.25rem;
}
.nb-btn,
.nb-btn-ghost {
  padding: 8px 16px;
  font: inherit;
  font-weight: 600;
  border-radius: 8px;
  cursor: pointer;
}
.nb-btn {
  border: none;
  background: var(--accent);
  color: #fff;
}
.nb-btn-ghost {
  border: 1px solid var(--border);
  background: none;
  color: var(--text-soft);
}

/* ── Shell 3 colonnes ─────────────────────────────────────────────────── */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--toc-w);
  align-items: start;
}
/* App pages (review / comments / journal) have no anchored-comment rail — drop the empty
   third column so their content isn't left off-balance beside 320px of dead space. */
.shell--full {
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
}

.sidebar,
.rail {
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
}
/* `dvh` tracks the mobile URL-bar; keep `vh` as the fallback for old engines. */
@supports (height: 100dvh) {
  .sidebar,
  .rail {
    height: calc(100dvh - var(--topbar-h));
  }
}
.sidebar {
  border-right: 1px solid var(--border);
  padding: 0.75rem 0.5rem 3rem;
  background: var(--bg-soft);
}
.rail {
  padding: 1.1rem 0.9rem 3rem;
}

.content {
  min-width: 0;
  padding: 1.25rem clamp(1rem, 4vw, 3rem) 5rem;
}
.content > .prose,
.content > .breadcrumb {
  max-width: var(--content-max);
  margin-inline: auto;
}

/* ── Site footer (config `nav.footer`) ────────────────────────────────────
   Emitted only when configured — nothing set → no element at all, so the output of
   every pre-footer site is unchanged. Outside `.shell` (full width, below the
   sticky columns) and outside `data-pagefind-body` (search never sees it). */
.site-footer {
  padding: 1.1rem clamp(1rem, 4vw, 3rem);
  border-top: 1px solid var(--border);
  background: var(--bg-soft);
  font-size: 13px;
  color: var(--text-soft);
}
.site-footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.4rem;
}
.site-footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.2rem;
}
.nb-footer-link {
  color: var(--text-soft);
}
.nb-footer-link:hover {
  color: var(--accent);
  text-decoration: none;
}
.site-footer-powered {
  margin-left: auto;
  color: var(--text-faint);
}

/* ── Sidebar nav ──────────────────────────────────────────────────────── */
.nav-filter {
  padding: 0.25rem 0.4rem 0.6rem;
}
.nav-filter input {
  width: 100%;
  height: 30px;
  padding: 0 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}
.nav-filter input:focus {
  outline: none;
  border-color: var(--accent);
}

.space {
  margin-bottom: 0.75rem;
}
.space > .space-title {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  cursor: pointer;
  list-style: none;
  border-radius: 6px;
}
.space > .space-title::-webkit-details-marker {
  display: none;
}
.space > .space-title:hover {
  background: var(--accent-soft);
}
.space-name {
  font-weight: 700;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.space[open] .space-name {
  color: var(--accent);
}
.space-sub {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-faint);
}
.space-link {
  margin: 0 0 0.25rem;
  padding-left: 0.75rem;
  font-size: 12px;
}

/* Resources block under the space tree (config `nav.sidebar`). The mobile drawer
   reuses this DOM as-is — no duplicate markup, unlike the topbar utils. */
.nb-sidebar-links {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 1rem 0 0;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}
.nb-sidebar-links-title {
  margin: 0 0 0.25rem;
  padding: 0 0.5rem;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}
.nb-sidebar-link {
  padding: 5px 8px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--text-soft);
}
.nb-sidebar-link:hover {
  background: var(--accent-soft);
  color: var(--accent);
  text-decoration: none;
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 0.55rem;
}
.nav-list .nav-list {
  border-left: 1px solid var(--border);
  margin-left: 0.35rem;
  padding-left: 0.5rem;
}
.nav-group > details > summary {
  display: block;
  padding: 3px 6px;
  cursor: pointer;
  font-weight: 600;
  font-size: 13px;
  color: var(--text-soft);
  border-radius: 5px;
  list-style: none;
}
.nav-group > details > summary::-webkit-details-marker {
  display: none;
}
.nav-group > details > summary::before {
  content: "▸";
  display: inline-block;
  width: 1em;
  font-size: 10px;
  color: var(--text-faint);
  transition: transform 0.12s;
}
.nav-group > details[open] > summary::before {
  transform: rotate(90deg);
}
.nav-group > details > summary:hover {
  background: var(--accent-soft);
}
.nav-a {
  display: block;
  padding: 3px 6px 3px 1.4em;
  font-size: 13px;
  color: var(--text-soft);
  border-radius: 5px;
}
.nav-a:hover {
  background: var(--accent-soft);
  color: var(--text);
  text-decoration: none;
}
.nav-a.active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

/* ── Breadcrumb ───────────────────────────────────────────────────────── */
.breadcrumb {
  font-size: 12.5px;
  color: var(--text-faint);
  margin-bottom: 1rem;
}
.breadcrumb .sep {
  margin: 0 0.4rem;
  opacity: 0.6;
}

/* ── TOC ──────────────────────────────────────────────────────────────── */
.toc-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  margin: 0 0 0.5rem;
}
.toc-inner ul {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 1px solid var(--border);
}
.toc-inner li a {
  display: block;
  padding: 2px 0 2px 0.75rem;
  font-size: 12.5px;
  color: var(--text-soft);
  border-left: 2px solid transparent;
  margin-left: -1px;
}
.toc-inner li.d3 a {
  padding-left: 1.5rem;
}
.toc-inner li a:hover {
  color: var(--accent);
  text-decoration: none;
}

/* ── Prose (contenu markdown) ─────────────────────────────────────────── */
.prose {
  word-wrap: break-word;
}
.prose h1 {
  font-size: 1.9rem;
  line-height: 1.2;
  margin: 0 0 1.2rem;
  letter-spacing: -0.01em;
}
.prose h2 {
  font-size: 1.4rem;
  margin: 2.2rem 0 0.9rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--border);
}
.prose h3 {
  font-size: 1.15rem;
  margin: 1.8rem 0 0.7rem;
}
.prose h4 {
  font-size: 1rem;
  margin: 1.4rem 0 0.5rem;
}
.prose h2,
.prose h3,
.prose h4 {
  scroll-margin-top: calc(var(--topbar-h) + 1rem);
}
.prose p,
.prose li {
  color: var(--text);
}
.prose a {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.prose code {
  font-family: var(--mono);
  font-size: 0.86em;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.1em 0.35em;
}
.prose pre {
  background: var(--code-bg) !important;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  overflow-x: auto;
  font-size: 13px;
  line-height: 1.5;
}
.prose pre code {
  background: none;
  border: none;
  padding: 0;
  font-size: inherit;
}
/* Mermaid diagrams: ```mermaid → pre.mermaid (src/remark/mermaid.mjs), rendered to SVG
   client-side (src/lib/client/mermaid.ts). Pre-render, show the source legibly as a
   graceful fallback; once rendered (.nb-mermaid-ok) it holds a centered, responsive SVG. */
.prose pre.mermaid {
  background: none !important;
  border: none;
  padding: 0;
  margin: 1.25rem 0;
  text-align: center;
  overflow-x: auto;
  font-family: var(--mono);
  color: var(--text-soft);
}
.prose pre.mermaid.nb-mermaid-ok {
  font-family: inherit;
  /* The rendered SVG already fits the column (Mermaid emits width:100% capped at its natural
     max-width), so it never needs to scroll. Drop the source-text `overflow-x: auto` here:
     as a scroll container it clips the comment count badge (::after, offset outside the box)
     and its overhang triggers a spurious horizontal scrollbar. Enlarge via the ⤢ lightbox. */
  overflow: visible;
}
.prose pre.mermaid svg {
  /* Defensive cap only — never upscales (Mermaid's own width:100%/max-width already fit the
     column); guards the rare diagram that emits a fixed px width wider than the container. */
  max-width: 100%;
  height: auto;
}
.prose pre.mermaid.nb-mermaid-error {
  text-align: left;
  color: crimson;
}

/* Block comments on diagrams/images: floating toolbar, commented-block badge, flash. */
.cmt-blockbar {
  position: fixed;
  /* Below the sticky .topbar (z-index 50) so it can never paint over the header. */
  z-index: 45;
  display: flex;
  gap: 4px;
  padding: 3px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 9px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
}
.cmt-blockbar button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  padding: 7px 9px;
  border-radius: 6px;
  color: var(--text);
}
.cmt-blockbar button svg {
  display: block;
}
.cmt-blockbar button:hover {
  background: var(--accent-soft);
}
.prose pre.mermaid.cmt-block-has,
.prose img.cmt-block-has {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
  /* The whole commented block opens its thread on click (openView → blockCommentIdsFor). */
  cursor: pointer;
}
/* Count badge (indication) on a commented diagram/image. A REAL element (built in
   Comments.astro markBlocks), not a ::after — pseudo-elements don't render on <img>
   (a replaced element), so images would silently get no badge. Body-level + document
   coords (positioned in JS); clicking it opens the block's thread. Sits at the block's
   top-left, clear of the top-right hover toolbar (action). */
.cmt-block-badge {
  position: absolute;
  z-index: 61;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  background: var(--accent);
  color: #fff;
  font: 600 12.5px/1 var(--sans);
  padding: 5px 9px;
  border: none;
  border-radius: 999px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.3);
  cursor: pointer;
}
.cmt-block-badge:hover {
  filter: brightness(1.08);
}
.prose pre.mermaid.cmt-block-flash,
.prose img.cmt-block-flash {
  animation: nb-block-flash 1.4s ease-out;
}
@keyframes nb-block-flash {
  0%,
  45% {
    outline: 3px solid var(--accent);
    outline-offset: 4px;
  }
  100% {
    outline: 3px solid transparent;
    outline-offset: 4px;
  }
}
.cmt-quote.cmt-block {
  font-style: normal;
}

/* Lightbox — enlarge a diagram/image over a dim backdrop and pan/zoom it. The stage is a
   fixed viewport; the content layer is transformed (pan/zoom) inside it by lib/client/
   lightbox.ts. Escape or the backdrop margin closes; the − % + ⤢ pill controls zoom. */
.nb-lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3vmin;
  background: rgba(0, 0, 0, 0.78);
}
.nb-lightbox[hidden] {
  display: none;
}
.nb-lightbox-stage {
  position: relative;
  width: 94vw;
  height: 90vh;
  overflow: hidden;
  background: var(--bg);
  border-radius: var(--radius);
  cursor: grab;
  touch-action: none; /* we own the gestures (pan/pinch) */
  overscroll-behavior: contain;
}
.nb-lightbox-stage.nb-grabbing {
  cursor: grabbing;
}
.nb-lightbox-content {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}
.nb-lightbox-content svg,
.nb-lightbox-content img {
  display: block;
  width: 100%;
  height: 100%;
  user-select: none;
  -webkit-user-drag: none;
}
.nb-lightbox-close {
  position: absolute;
  top: 12px;
  right: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  font-size: 18px;
  cursor: pointer;
}
.nb-lightbox-close:hover {
  background: rgba(255, 255, 255, 0.28);
}
.nb-lightbox-tools {
  position: absolute;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(20, 22, 28, 0.82);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(6px);
}
.nb-lightbox-tools button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.nb-lightbox-tools button:hover {
  background: rgba(255, 255, 255, 0.18);
}
.nb-lightbox-pct {
  min-width: 54px;
  text-align: center;
  color: #fff;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.prose blockquote {
  margin: 1rem 0;
  padding: 0.4rem 1rem;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 0 6px 6px 0;
  color: var(--text-soft);
}
.prose blockquote p {
  margin: 0.3rem 0;
}
.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 1rem 0;
  font-size: 13.5px;
  display: block;
  overflow-x: auto;
}
/* Diff tables are fixed-layout tables meant to FILL their column (50/50 split). Opt out of
   the responsive `display:block` above — as a block box the table content stays at its
   natural width and leaves empty space to the right on the (now wider) content. */
.prose .nb-diff {
  display: table;
  width: 100%;
}
.prose th,
.prose td {
  border: 1px solid var(--border);
  padding: 0.45rem 0.7rem;
  text-align: left;
  vertical-align: top;
}
.prose th {
  background: var(--bg-soft);
  font-weight: 600;
}
/* An improvised ROW header — the only kind GFM allows. Markdown has no row-header cell, so
   the author writes `| **Label** | … |`, which still reads as a label on GitHub or in any
   other viewer, and here it also gets the header row's own surface. No new syntax, nothing
   to configure, and deleting the bold deletes the styling.
   Triggered by the table toolbar's header-row / header-column actions, which bold the WHOLE
   row or column — the only thing Markdown can carry. A lone bold cell is emphasis and is
   left alone. The class is decided in src/rehype/table-row-header.mjs, NOT by a selector:
   CSS can see neither text nodes nor "is every cell of this column bold?".
   Consumers who dislike the tint override `.prose td.nb-cellhead` in their theme.css. */
.prose td.nb-cellhead {
  background: var(--bg-soft);
}
.prose img {
  max-width: 100%;
  border-radius: 6px;
}
.prose hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 2rem 0;
}
.prose ul,
.prose ol {
  padding-left: 1.4rem;
}
.prose li {
  margin: 0.2rem 0;
}

/* ── Lede + cartes (index d'espace + accueil) ─────────────────────────── */
.lede {
  font-size: 1.05rem;
  color: var(--text-soft);
  margin: -0.4rem 0 1.6rem;
}
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 1rem;
}
.card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  background: var(--bg-elev);
}
.card h3 {
  margin: 0 0 0.5rem;
  font-size: 1rem;
}
.card ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.card li {
  margin: 0.15rem 0;
  font-size: 13.5px;
}
.card .more {
  margin: 0.5rem 0 0;
  font-size: 12px;
  color: var(--text-faint);
}

.home-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  margin-top: 1.5rem;
}
.home-card {
  display: block;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1.5rem;
  background: var(--bg-elev);
  color: var(--text);
  transition: border-color 0.15s, transform 0.15s;
}
.home-card:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
  text-decoration: none;
}
.home-card h2 {
  margin: 0 0 0.3rem;
}
.home-card p {
  margin: 0.2rem 0;
  color: var(--text-soft);
}

/* ── Custom 404 (src/pages/404.astro) ─────────────────────────────────────── */
.nf {
  text-align: center;
  padding: 2.5rem 1rem 0.5rem;
}
.nf-code {
  margin: 0;
  font-size: clamp(4rem, 13vw, 7rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--accent);
}
.nf h1 {
  margin: 0.25rem 0 0.5rem;
  font-size: 1.6rem;
}
.nf .lede {
  margin: 0 auto 1.25rem;
  max-width: 34rem;
}
.nf-path {
  margin: 0 0 1.5rem;
  font-size: 13px;
  color: var(--text-faint);
}
.nf-path code {
  padding: 2px 8px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-soft);
  font-family: var(--mono);
  word-break: break-all;
}
.nf-actions {
  margin: 0;
}
.nf-home {
  display: inline-block;
  padding: 0.55rem 1.4rem;
  background: var(--accent);
  border-radius: 999px;
  color: #fff;
  font-weight: 600;
}
.nf-home:hover {
  filter: brightness(1.06);
  text-decoration: none;
}
.nf-spaces-label {
  margin: 2.25rem 0 0;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}

/* ── Responsive shell ─────────────────────────────────────────────────────
   Desktop ≥1024: the 3-col grid above, unchanged.
   Compact  <1024: nav → off-canvas drawer, TOC → inline collapsible,
                   anchored comments → bottom sheet (the .rail / #cmt-pop /
                   #cmt-view surfaces re-anchored to the bottom).
   Phone    <560:  extra condensing.
   Shared scrim + mobile-only chrome are declared here and hidden on desktop. */

/* Mobile-only chrome — hidden on desktop. */
.nb-nav-toggle,
.nb-cmt-sheet-btn,
.toc-inline,
.drawer-util {
  display: none;
}

/* Shared backdrop behind the drawer and the bottom sheets. */
.nb-scrim {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.45);
  border: none;
}

@media (max-width: 1024px) {
  /* Single reading column; the rail leaves the grid (it becomes a bottom sheet). */
  .shell {
    grid-template-columns: minmax(0, 1fr);
  }
  .rail {
    display: none;
  }

  /* Topbar: hamburger + 💬 in; utility links + name move into the drawer. */
  .nb-nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px;
    margin-left: -4px;
    background: none;
    border: none;
    border-radius: var(--radius);
    color: var(--text);
    cursor: pointer;
  }
  .nb-nav-toggle:hover {
    background: var(--accent-soft);
  }
  .topbar-util {
    display: none;
  }
  .nb-cmt-sheet-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    padding: 5px 11px;
    background: none;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-soft);
    font-size: 13px;
    white-space: nowrap;
    cursor: pointer;
  }
  .nb-cmt-sheet-count:empty {
    display: none;
  }

  /* Off-canvas nav drawer (the existing .sidebar, re-anchored + slid off-screen). */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: min(84vw, 320px);
    height: 100dvh;
    z-index: 110;
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    border-right: 1px solid var(--border);
    padding-top: calc(env(safe-area-inset-top) + 0.75rem);
    will-change: transform;
  }
  .sidebar.nb-open {
    transform: translateX(0);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.3);
  }
  .drawer-util {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
  }
  .drawer-link {
    padding: 10px 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-soft);
    border-radius: 6px;
  }
  .drawer-link:hover {
    background: var(--accent-soft);
    text-decoration: none;
  }
  .drawer-me {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    padding: 6px 8px;
  }
  .drawer-me input {
    flex: 1;
    min-height: 40px;
    padding: 6px 10px;
    font-size: 16px;
    color: var(--text);
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: var(--radius);
  }

  /* Inline "On this page" collapsible, above the article. */
  .toc-inline {
    display: block;
    max-width: var(--content-max);
    margin: 0 auto 1.25rem;
    padding: 0 0.9rem;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: var(--radius);
  }
  .toc-inline > summary {
    padding: 0.6rem 0;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-faint);
    list-style: none;
    cursor: pointer;
  }
  .toc-inline > summary::-webkit-details-marker {
    display: none;
  }
  .toc-inline .toc-title {
    display: none;
  }
  .toc-inline .toc-inner {
    padding-bottom: 0.6rem;
  }

  /* ── Bottom sheets: the existing fixed surfaces, re-anchored to the bottom ──
     --nb-kb (set by shell.ts from VisualViewport) lifts the sheet above the
     on-screen keyboard. Show/hide is the elements' own `hidden`/`.nb-sheet`. */
  .rail.nb-sheet,
  #cmt-pop:not([hidden]),
  #cmt-view:not([hidden]) {
    position: fixed;
    inset: auto 0 var(--nb-kb, 0px) 0;
    width: auto;
    max-width: none;
    /* Never taller than the space actually left above the keyboard (see --nb-sheet-max
       in shell.ts); 85svh is only the ceiling when there is no keyboard. */
    max-height: min(85svh, var(--nb-sheet-max, 85svh));
    z-index: 110;
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.28);
    padding-bottom: calc(env(safe-area-inset-bottom) + 12px);
    overflow-y: auto;
    overscroll-behavior: contain;
    touch-action: pan-y;
  }
  .rail.nb-sheet {
    display: block;
    padding: 12px 14px calc(env(safe-area-inset-bottom) + 12px);
    /* The rail is a column in the page on desktop, so it never needed a background of
       its own — it simply sat on the page's. As a bottom SHEET it overlays the article,
       and without this the text scrolled visibly underneath it. `#cmt-pop` and
       `#cmt-view`, which are overlays on every viewport, already carry the same token. */
    background: var(--bg-elev);
  }
  .rail .toc-inner {
    display: none; /* the TOC lives inline on compact, not in the sheet */
  }
  /* Drag handle. It used to be a `::before` — a pill that looks grabbable and does
     nothing, which is worse than no affordance at all. It is now a real button
     (injected by shell.ts, which owns the drag): a pseudo-element cannot opt out of
     the sheet's `touch-action: pan-y`, so the browser would steal every vertical drag
     before any JS ran. The pill is drawn with ::before INSIDE the button; the button
     itself is a comfortable 28px target spanning the sheet's width. */
  .nb-sheet-grip {
    display: block;
    width: 100%;
    height: 28px;
    padding: 0;
    margin: -4px 0 6px;
    border: 0;
    background: none;
    cursor: grab;
    touch-action: none;
  }
  .nb-sheet-grip:active {
    cursor: grabbing;
  }
  .nb-sheet-grip::before {
    content: "";
    display: block;
    width: 36px;
    height: 4px;
    margin: 10px auto 0;
    border-radius: 999px;
    background: var(--border);
  }
  .nb-sheet-grip:hover::before,
  .nb-sheet-grip:focus-visible::before {
    background: var(--text-faint);
  }
  /* Expanded detent: fill the sheet's allowance instead of hugging the content. */
  .rail.nb-sheet.nb-sheet-full,
  #cmt-pop.nb-sheet-full:not([hidden]),
  #cmt-view.nb-sheet-full:not([hidden]) {
    height: min(85svh, var(--nb-sheet-max, 85svh));
  }
  /* Expanding the composer must give the extra room to the TEXTAREA — otherwise the
     sheet just grows a dead grey area under a three-line form. */
  #cmt-pop.nb-sheet-full:not([hidden]) textarea {
    flex: 1;
  }
  #cmt-pop textarea {
    font-size: 16px; /* ≥16px prevents iOS focus-zoom */
  }

}

@media (max-width: 560px) {
  .topbar {
    gap: 0.6rem;
    padding: 0 0.6rem;
  }
  .brand span {
    display: none; /* drop the tagline on very small screens */
  }
  .rail.nb-sheet,
  #cmt-view:not([hidden]) {
    max-height: 90svh;
  }
}

/* Force unified diffs on narrow screens. The toggle is NOT hidden here: this rule sits
   before `.diff-toggle { display: inline-flex }` and has the same specificity (a media
   query adds none), so source order won and the button stayed visible while the mode was
   coerced. review-card.ts now paints the toggle only when the same 640px predicate is
   false — one source of truth instead of two. */
@media (max-width: 640px) {
  .nb-diff {
    font-size: 0.75rem;
  }
}

/* ── Touch targets: enlarge interactive controls for coarse pointers ─────── */
@media (pointer: coarse) {
  .nav-a,
  .space > .space-title,
  .nav-group > details > summary,
  .topbar-link,
  .drawer-link {
    padding-top: 9px;
    padding-bottom: 9px;
  }
  .cmt-filter button,
  .diff-toggle button {
    min-height: 40px;
    padding: 6px 14px;
  }
  .cmt-actions button,
  .caf-actions button,
  .rev-actions button {
    min-height: 44px;
    min-width: 44px;
    justify-content: center;
    padding: 8px;
  }
  .cmt-actions,
  .caf-actions {
    gap: 4px;
    flex-wrap: wrap;
  }
  .caf-filters input[type="search"],
  .caf-filters select {
    min-height: 44px;
  }
  .cmt-composer button,
  .cmt-reply-form button,
  .cmt-edit-form button,
  .cmt-pop-actions button,
  .rev-reject-form button {
    min-height: 44px;
    padding: 8px 16px;
  }
  .cmt-actions button svg,
  .caf-actions button svg,
  .rev-actions button svg {
    width: 18px;
    height: 18px;
  }
  textarea {
    font-size: 16px; /* avoid iOS focus-zoom */
  }
}

/* Respect reduced-motion for the drawer slide. */
@media (prefers-reduced-motion: reduce) {
  .sidebar {
    transition: none;
  }
}

/* ── Comments + highlights ────────────────────────────────────────────── */
::highlight(cmt) {
  background: rgba(255, 200, 0, 0.42);
  text-decoration: underline solid var(--accent);
}
::highlight(cmt-active) {
  background: rgba(255, 145, 0, 0.55);
}

.comments {
  margin-top: 1.5rem;
  border-top: 1px solid var(--border);
  padding-top: 1rem;
}
.cmt-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
.cmt-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
}
.cmt-count {
  font-size: 11px;
  color: var(--text-faint);
}
.cmt-filter {
  display: flex;
  gap: 4px;
  margin: 0.5rem 0;
}
.cmt-filter button {
  font-size: 11px;
  padding: 2px 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-soft);
  border-radius: 999px;
  cursor: pointer;
}
.cmt-filter button.active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.cmt-pending {
  font-size: 11.5px;
  background: var(--accent-soft);
  border-radius: 6px;
  padding: 5px 8px;
  margin-bottom: 6px;
  color: var(--text-soft);
}
.cmt-pending button {
  float: right;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-faint);
}
.cmt-composer {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 1rem;
}
.cmt-composer textarea,
.cmt-reply-form textarea,
.cmt-edit-form textarea {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font: inherit;
  font-size: 13px;
  background: var(--bg);
  color: var(--text);
  resize: vertical;
}
.cmt-composer button,
.cmt-reply-form button,
.cmt-edit-form button {
  align-self: flex-end;
  font-size: 12px;
  padding: 4px 12px;
  border: none;
  border-radius: 6px;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
}
.cmt-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cmt-empty {
  font-size: 12.5px;
  color: var(--text-faint);
}
.cmt-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  background: var(--bg-elev);
  font-size: 13px;
}
.cmt-card.resolved {
  opacity: 0.6;
}
.cmt-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
}
.cmt-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.cmt-dot.open {
  background: #e0a000;
}
.cmt-dot.addressed {
  background: var(--accent);
}
.cmt-dot.resolved {
  background: #3aa55d;
}
.cmt-tovalidate {
  font-size: 0.72rem;
  color: var(--accent);
  white-space: nowrap;
}
.cmt-quote {
  font-size: 12px;
  color: var(--accent);
  cursor: pointer;
}
.cmt-quote.orphan {
  color: #c0392b;
}
.cmt-scope {
  font-size: 12px;
  color: var(--text-faint);
}
.cmt-body {
  color: var(--text);
  word-wrap: break-word;
}
.cmt-reply {
  margin-top: 4px;
  padding-left: 8px;
  border-left: 2px solid var(--border);
  color: var(--text-soft);
}
.cmt-resolution {
  margin-top: 5px;
  font-size: 12px;
  color: #3aa55d;
  background: rgba(58, 165, 93, 0.1);
  padding: 3px 6px;
  border-radius: 4px;
}
.cmt-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.cmt-ts {
  font-size: 10.5px;
  color: var(--text-faint);
  margin-right: auto;
}
.cmt-actions button {
  display: inline-flex;
  align-items: center;
  border: none;
  background: none;
  color: var(--accent);
  cursor: pointer;
  padding: 2px;
}
.cmt-actions button:hover {
  opacity: 0.6;
}
.cmt-actions button.danger {
  color: var(--text-faint);
}
.cmt-reply-form,
.cmt-edit-form {
  margin-top: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.cmt-float {
  position: fixed;
  z-index: 60;
  font-size: 12px;
  padding: 4px 10px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  /* Never let a press on the affordance extend/replace the selection or fire a callout. */
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Discussion (page-wide comments) — at the bottom of the page, aligned with prose. */
.cmt-page {
  max-width: var(--content-max);
  margin: 3rem auto 0;
  border-top: 2px solid var(--border);
  padding-top: 1.5rem;
}
.cmt-page-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 1rem;
}
.cmt-page-head h2 {
  margin: 0;
  font-size: 1.2rem;
  border: none;
  padding: 0;
}
/* The discussion (composer + page-comment cards) fills the content width like the rest of
   the page — no narrower cap, so it lines up with the prose/tables above it. */

/* Right rail — anchored annotations. */
.cmt-rail {
  margin-top: 1.5rem;
  border-top: 1px solid var(--border);
  padding-top: 1rem;
}
.cmt-rail-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 0.4rem;
}
.cmt-rail-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
}
.cmt-rail-count {
  font-size: 11px;
  color: var(--text-faint);
}

/* Creation popover on selection. */
.cmt-pop {
  position: fixed;
  z-index: 70;
  width: 300px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.25);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.cmt-pop-quote {
  font-size: 12px;
  color: var(--text-soft);
  background: var(--accent-soft);
  border-radius: 5px;
  padding: 5px 7px;
  max-height: 64px;
  overflow: auto;
}
.cmt-pop textarea {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font: inherit;
  font-size: 13px;
  background: var(--bg);
  color: var(--text);
  resize: vertical;
}
.cmt-pop-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.cmt-pop-actions button {
  font-size: 12px;
  padding: 4px 12px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-soft);
  cursor: pointer;
}
.cmt-pop-actions #cmt-pop-save {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* READING popover (click on highlighted text) — shows the comment(s). */
.cmt-view {
  position: fixed;
  z-index: 70;
  width: 320px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.3);
  padding: 8px;
}
.cmt-view .cmt-card {
  border: none;
  background: none;
  padding: 4px 2px;
}
.cmt-view .cmt-card + .cmt-card {
  border-top: 1px solid var(--border);
  margin-top: 4px;
}

/* ── Journal des modifications ────────────────────────────────────────── */
.journal {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.journal-entry {
  border-left: 2px solid var(--accent);
  padding-left: 1rem;
}
.journal-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}
.journal-head h2 {
  margin: 0;
  font-size: 1.1rem;
  border: none;
  padding: 0;
}
.journal-date {
  font-size: 12px;
  color: var(--text-faint);
  font-family: var(--mono);
}
.journal-summary {
  color: var(--text-soft);
  white-space: pre-wrap;
  margin: 0.4rem 0;
}
.journal-changes {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.journal-changes li {
  font-size: 13.5px;
}
.journal-page {
  font-family: var(--mono);
  font-size: 12.5px;
}
.journal-why {
  color: var(--text-soft);
  margin-top: 2px;
}
.journal-cids {
  font-size: 11px;
  color: var(--text-faint);
  font-family: var(--mono);
  margin-top: 2px;
}

/* ── En attente (hold) ────────────────────────────────────────────────── */
.cmt-hold-badge {
  font-size: 10.5px;
  color: #c98a00;
  background: rgba(201, 138, 0, 0.13);
  border-radius: 999px;
  padding: 1px 7px;
}
.cmt-card.held,
.caf-row.held {
  opacity: 0.72;
  border-style: dashed;
}

/* ── Page globale /comments ───────────────────────────────────────────── */
.caf-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 1rem 0 1.25rem;
}
.caf-filters input[type="search"] {
  flex: 1;
  min-width: 200px;
  height: 32px;
  padding: 0 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}
.caf-filters select {
  height: 32px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  padding: 0 6px;
}
.caf-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.caf-row {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  background: var(--bg-elev);
  font-size: 13.5px;
}
.caf-row.resolved {
  opacity: 0.6;
}
.caf-row-top {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.caf-page {
  font-family: var(--mono);
  font-size: 12.5px;
}
.caf-quote {
  color: var(--accent);
  font-size: 12.5px;
}
.caf-replies {
  font-size: 11px;
  color: var(--text-faint);
}
.caf-body {
  color: var(--text);
}
.caf-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
}
.caf-actions .cmt-ts {
  margin-right: auto;
}
.caf-actions button {
  display: inline-flex;
  align-items: center;
  border: none;
  background: none;
  color: var(--accent);
  cursor: pointer;
  padding: 2px;
}
.caf-actions button:hover {
  opacity: 0.6;
}
.caf-actions button.danger {
  color: var(--text-faint);
}

/* ── Two-phase review: badge, cards, diff ────────────────────── */
.review-badge {
  display: inline-block;
  min-width: 1.2em;
  padding: 0 0.4em;
  border-radius: 999px;
  background: var(--accent);
  color: var(--bg);
  font-size: 0.75em;
  font-weight: 600;
  text-align: center;
}
.review-badge:empty {
  display: none;
}
.rev-toolbar {
  display: flex;
  justify-content: flex-end;
  margin: 12px 0;
}
/* No cards in the queue → review-card.ts empties the toggle rather than leaving an inert
   control that highlights a mode and changes nothing. */
.diff-toggle:empty {
  display: none;
}
.diff-toggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.diff-toggle button {
  padding: 4px 12px;
  background: var(--bg-soft);
  color: var(--text-soft);
  border: 0;
  cursor: pointer;
  font-size: 0.85rem;
}
.diff-toggle button.active {
  background: var(--accent);
  color: var(--bg);
}
.rev-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.rev-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev);
  padding: 14px 16px;
}
.rev-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.rev-page {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--accent);
}
.rev-quote {
  color: var(--text-soft);
  font-style: italic;
}
.rev-thread {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 8px;
}
.rev-msg {
  font-size: 0.9rem;
}
.rev-agent {
  font-size: 0.85rem;
  color: var(--text-soft);
  margin-bottom: 10px;
}
.rev-change {
  margin: 10px 0;
}
.rev-change-head {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text-faint);
  margin-bottom: 4px;
}
.rev-also {
  font-size: 0.8rem;
  color: var(--text-faint);
  margin-top: 6px;
}
.rev-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
}
.rev-actions .rev-jump {
  margin-right: auto;
  font-size: 0.85rem;
  color: var(--text-soft);
}
.rev-actions button {
  padding: 5px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-soft);
  color: var(--text);
  cursor: pointer;
  font-size: 0.85rem;
}
.rev-actions button.primary {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}
.rev-reject-form {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.rev-reject-form textarea {
  flex: 1;
}
.nb-diff {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  table-layout: fixed;
}
.nb-diff td {
  padding: 0 8px;
  white-space: pre-wrap;
  word-break: break-word;
  vertical-align: top;
}
.nb-diff tr.hunk td,
.nb-diff td.hunk {
  background: var(--bg-soft);
  color: var(--text-faint);
}
.nb-diff .sign {
  display: inline-block;
  width: 1ch;
  color: var(--text-faint);
}
.nb-diff tr.add,
.nb-diff td.add {
  background: rgba(46, 160, 67, 0.15);
}
.nb-diff tr.del,
.nb-diff td.del {
  background: rgba(248, 81, 73, 0.15);
}
.nb-diff-split td {
  width: 50%;
  border-left: 1px solid var(--border);
}
.nb-diff td.empty {
  background: var(--bg-soft);
}

/* ── public build: screen-reader/agent-only content ─────────────────────────── */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── branding: topbar logo (config `branding.logo` / `.logoDark`) ───────────── */
.brand-logo {
  height: 24px;
  width: auto;
  display: inline-block;
  vertical-align: -6px;
  margin-right: 8px;
  border-radius: 4px;
}
/* Logo variant swap — forcing-aware: the media query only applies when no manual
   scheme is forced; the [data-scheme] rules take over otherwise. */
.brand-logo--dark {
  display: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-scheme="light"]) .brand-logo--light {
    display: none;
  }
  :root:not([data-scheme="light"]) .brand-logo--dark {
    display: inline-block;
  }
}
:root[data-scheme="dark"] .brand-logo--light {
  display: none;
}
:root[data-scheme="dark"] .brand-logo--dark {
  display: inline-block;
}

/* ── scheme toggle (topbar + drawer) — icon follows the STORED preference:
   auto (half disc) / light (sun) / dark (moon), driven by [data-scheme]. ── */
button.nb-scheme-toggle {
  font-family: inherit;
  background: none;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 7px;
  border-radius: 6px;
}
button.nb-scheme-toggle:hover {
  color: var(--text);
  background: var(--bg-soft);
}
.nb-scheme-toggle svg {
  display: none;
}
:root:not([data-scheme]) .nb-scheme-icon--auto {
  display: block;
}
:root[data-scheme="light"] .nb-scheme-icon--light {
  display: block;
}
:root[data-scheme="dark"] .nb-scheme-icon--dark {
  display: block;
}

/* ── page footer meta: last-updated + "Edit this page" (config editPattern) ── */
.page-meta {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 2.5rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--text-faint);
}
.page-meta-edit {
  margin-left: auto;
  color: var(--text-faint);
}
.page-meta-edit:hover {
  color: var(--accent);
}

/* ── in-page editor (dev only) ─────────────────────────────────────────────
   Editing has no chrome of its own. The surface must be visually INDISTINGUISHABLE
   from the rendered block — no border, no background, no padding, no box — because
   anything else makes the page jump the moment you click, which is exactly what a
   block editor must not do. The only new things on screen are a caret, a hover
   handle in the gutter, and a toolbar that appears at the selection.

   These rules ship in the shared stylesheet like every other component's (.cmt-*
   included), inert in any build: no build ever emits a stamp. Moving them into
   Editor.astro was tried and is WORSE — an `is:global` component style gets inlined
   into every page's HTML instead of the one bundled sheet. The CI canary therefore
   greps for an actual stamp (`data-nb-range="`), not for the bare token. */

/* Editor CHROME surface. --bg-elev is nearly the page colour in the dark scheme
   (#161b22 on #0e1116), so every toolbar and menu melted into the content and nothing
   read as interactive (user feedback). A step of the TEXT colour mixed into the
   surface lifts it in either scheme — text is the one colour guaranteed to contrast
   with the background — and a crisper border separates chrome from prose. Internal
   aliases derived entirely from the --nb-* contract, never new public tokens. */
:root {
  --chrome-bg: color-mix(in srgb, var(--text) 8%, var(--bg-elev));
  --chrome-border: color-mix(in srgb, var(--text) 18%, var(--border));
}

/* Editable blocks stay COMPLETELY unstyled until opened — reading must not look like an
   editor. Hover reveals the gutter handles and nothing else: a hover tint was tried and
   read as a selection (Notion tints nothing either; the handles are the affordance). */
.nb-edit-handle {
  position: absolute;
  z-index: 40;
  width: 28px;
  height: 28px;
  /* The visible button is 28px, but its HIT AREA reaches back to the block it belongs
     to (::after below) — so the pointer never crosses dead space on its way here. */
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  color: var(--text);
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
  border-radius: 6px;
  cursor: pointer;
  box-shadow: 0 2px 6px rgb(0 0 0 / 10%);
}
.nb-edit-handle::after {
  content: "";
  position: absolute;
  top: -6px;
  bottom: -6px;
  left: 100%;
  width: 10px;
}
.nb-edit-handle:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.nb-edit-handle svg {
  width: 15px;
  height: 15px;
}

/* The live surface. `display: contents` would be ideal but breaks contenteditable,
   so instead every wrapper contributes exactly nothing to layout and the inner
   blocks inherit the article's own typography — margins collapse through as if the
   editor were not there. */
.nb-edit-live,
.nb-edit-live .milkdown,
.nb-edit-live .nb-wysiwyg-area {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  outline: none;
  font: inherit;
  color: inherit;
}
.nb-edit-live .nb-wysiwyg-area {
  min-height: 1.6em;
  /* What prosemirror-view's own stylesheet would set (we cannot import it here — this
     file ships in every build and the CI canary greps for that token): the editable
     surface must preserve whitespace or consecutive spaces silently collapse. */
  white-space: pre-wrap;
  overflow-wrap: break-word;
  font-variant-ligatures: none;
}
/* ProseMirror wraps the content of every cell and list item in a real <p>, where the
   rendered page leaves it bare inline. Left alone, `.prose p`'s 13.5px margins add 27px
   to every table row and list item — the one place where "identical to the rendered
   block" visibly broke (64px rows against 37px). Everything else about these tables,
   borders and padding included, already comes from `.prose` and matches exactly. */
.nb-edit-live :is(td, th, li) > p {
  margin: 0;
}
/* Caret only — no focus ring. The block is the page; a ring would redraw its box. */
.nb-edit-live .nb-wysiwyg-area:focus,
.nb-edit-live .nb-wysiwyg-area:focus-visible {
  outline: none;
}
/* Source fallback (⌘⇧M): monospace, but still box-free. */
.nb-edit-source {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.92em;
  line-height: 1.7;
  resize: none;
  outline: none;
  overflow: hidden;
}

/* Selection toolbar — small, at the selection, gone the moment nothing is selected. */
.nb-edit-float {
  position: absolute;
  z-index: 60;
  display: flex;
  gap: 2px;
  padding: 3px;
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
  border-radius: 7px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 18%);
}
/* Notion's answer to "how would anyone know?": the empty block says what to press.
   ProseMirror renders an empty paragraph as `<p><br class="ProseMirror-trailingBreak"></p>`,
   so `:empty` never matches — `:has()` on that break is what identifies it. The text rides
   in on an inherited custom property because it has to be translatable, and `attr()` only
   reads the pseudo-element's OWN element.

   The CHILD combinator is load-bearing: every cell of a fresh table is an empty paragraph
   too, so a descendant selector painted the hint into all nine of them at once, overlapping
   into an unreadable smear. Only a top-level block gets the prompt. */
.nb-wysiwyg-area > p:has(> br.ProseMirror-trailingBreak:only-child)::before {
  content: var(--nb-placeholder, "");
  position: absolute;
  color: var(--text-soft);
  opacity: 0.62;
  pointer-events: none;
}
.nb-wysiwyg-area > p:has(> br.ProseMirror-trailingBreak:only-child) {
  position: relative;
}
/* The `/` palette. Same surface language as the floating bar — it is the same kind of
   thing: transient chrome anchored to the caret, never part of the reading column.
   Visibility is `data-show`, flipped by Milkdown's SlashProvider (which also positions
   the element), not `hidden` — the provider owns this element's lifecycle. */
.nb-edit-menu {
  position: absolute;
  z-index: 60;
  display: flex;
  flex-direction: column;
  min-width: 264px;
  max-height: 320px;
  overflow-y: auto;
  /* The default scrollbar is a wide, bright slab on a 190px popover — louder than the list
     it is scrolling. The standard properties are the whole answer: setting EITHER of them
     makes Chrome drop ::-webkit-scrollbar entirely, so the two cannot be combined. Most of
     the noise was the bright track anyway, not the width. */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
  padding: 4px;
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 18%);
}
/* Safari before 18.4 has neither standard property; there, and only there, the WebKit
   pseudo-elements are still the way to calm it down. */
@supports not (scrollbar-width: thin) {
  .nb-edit-menu::-webkit-scrollbar {
    width: 6px;
  }
  .nb-edit-menu::-webkit-scrollbar-track {
    background: transparent;
  }
  .nb-edit-menu::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
  }
  .nb-edit-menu::-webkit-scrollbar-thumb:hover {
    background: var(--text-soft);
  }
}
.nb-edit-menu:not([data-show="true"]) {
  display: none;
}
.nb-edit-menu-sect {
  padding: 6px 10px 2px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.nb-edit-menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 10px;
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.nb-edit-menu-item:hover,
.nb-edit-menu-item.is-active {
  background: var(--accent-soft);
}
.nb-edit-menu-ic {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg);
  color: var(--text-soft);
}
.nb-edit-menu-ic svg {
  width: 14px;
  height: 14px;
}
.nb-edit-menu-txt {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.nb-edit-menu-desc {
  font-size: 11px;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The honest shortcut: the Markdown that types the same block. */
.nb-edit-menu-item kbd {
  margin-left: auto;
  padding: 1px 5px;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--text-faint);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
}
/* The gutter + and ⋮⋮ sit further out than the ✎ and stay quieter until reached for. */
.nb-edit-handle--add,
.nb-edit-handle--menu {
  opacity: 0.55;
}
.nb-edit-handle--add:hover,
.nb-edit-handle--menu:hover {
  opacity: 1;
}
.nb-edit-fmt {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 6px;
  font: inherit;
  font-size: 13px;
  line-height: 1;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.nb-edit-fmt svg {
  width: 16px;
  height: 16px;
}
.nb-edit-fmt:hover {
  color: var(--accent);
  background: var(--accent-soft);
}
/* Active marks read as pressed — the answer to "is this already bold?" the glyph-only
   bar never gave. */
.nb-edit-fmt.is-active {
  color: var(--accent);
  background: var(--accent-soft);
}
/* Turn into leads the bar, as a labelled control: the label IS the current block type,
   which doubles as the bar's only state readout (Notion's "Normal text ▾"). */
.nb-edit-fmt--turninto {
  gap: 4px;
  padding: 0 8px;
  border-right: 1px solid var(--border);
  border-radius: 5px 0 0 5px;
  margin-right: 2px;
  white-space: nowrap;
}
.nb-edit-fmt--turninto .nb-edit-fmt-chevron {
  display: flex;
}
.nb-edit-fmt--turninto .nb-edit-fmt-chevron svg {
  width: 12px;
  height: 12px;
}
/* Header toggles only make sense in a table; the border groups them apart. */
.nb-edit-fmt--headerRow,
.nb-edit-fmt--outdent {
  border-left: 1px solid var(--border);
  margin-left: 2px;
  border-radius: 0 5px 5px 0;
}

/* The block being edited must READ as being edited — same metrics, different surface.
   Padding is cancelled by an equal negative margin, so the tint and the accent edge
   cost exactly zero layout shift. */
.nb-edit-live {
  padding: 4px 10px;
  margin: -4px -10px;
  border-radius: 6px;
  background: var(--bg-soft);
  box-shadow: inset 2px 0 0 var(--accent);
}

/* The session card, directly under the block, IN FLOW in the reading column. It replaced
   a vertical rail + far-left panel that put the cancel confirmation 800px from the hand
   asking for it. Compact when the block is untouched (one row of actions); the loop
   controls appear underneath once there is a change to attach them to. */
.nb-edit-card {
  margin: 0.5rem 0 1rem;
  padding: 0.45rem 0.55rem;
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
  border-radius: 8px;
  box-shadow: 0 4px 14px rgb(0 0 0 / 10%);
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-soft);
}
.nb-edit-card-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}
.nb-edit-card-spring {
  flex: 1;
}
.nb-edit-card-body {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.45rem;
  padding-top: 0.45rem;
  border-top: 1px solid var(--border);
}
.nb-edit-card-body[hidden] {
  display: none;
}
.nb-edit-card-body p {
  margin: 0;
}
.nb-edit-card-title {
  color: var(--text-faint);
}
.nb-edit-act {
  display: flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 9px;
  font: inherit;
  font-size: 12.5px;
  line-height: 1;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.nb-edit-act svg {
  width: 14px;
  height: 14px;
}
.nb-edit-act:hover {
  color: var(--accent);
  background: var(--accent-soft);
}
.nb-edit-act--done {
  color: var(--accent);
  font-weight: 600;
}
/* Cancel is the only control here that destroys work, so it never wears the accent —
   and once armed it says so in the one place the eye is already looking. Same literal
   as .nb-edit-msg[data-kind="error"]: there is no danger token, and inventing one here
   would put a colour outside the documented --nb-* surface. */
.nb-edit-act--cancel:hover {
  color: #c0392b;
  background: color-mix(in srgb, #c0392b 12%, transparent);
}
.nb-edit-act--armed,
.nb-edit-act--armed:hover {
  color: #c0392b;
  background: color-mix(in srgb, #c0392b 16%, transparent);
  font-weight: 600;
}

/* The ⋮⋮ block menu and the toolbar's Turn into menu — one popover language. */
.nb-block-menu {
  position: absolute;
  z-index: 60;
  display: flex;
  flex-direction: column;
  min-width: 200px;
  padding: 4px;
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 18%);
}
.nb-block-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 9px;
  font: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.nb-block-menu-item:hover {
  background: var(--accent-soft);
}
.nb-block-menu-ic {
  display: flex;
  flex: none;
  color: var(--text-soft);
}
.nb-block-menu-ic svg {
  width: 14px;
  height: 14px;
}
.nb-block-menu-check {
  display: flex;
  margin-left: auto;
  color: var(--accent);
}
.nb-block-menu-check svg {
  width: 13px;
  height: 13px;
}
.nb-block-menu-item--danger {
  color: #c0392b;
}
.nb-block-menu-item--danger .nb-block-menu-ic {
  color: inherit;
}
.nb-block-menu-item--danger:hover,
.nb-block-menu-item--armed {
  color: #c0392b;
  background: color-mix(in srgb, #c0392b 12%, transparent);
}

/* Transient confirmation near the action (link copied, a refusal from the menu). */
.nb-toast {
  position: absolute;
  z-index: 80;
  max-width: 22rem;
  padding: 5px 9px;
  border-radius: 6px;
  background: var(--text);
  color: var(--bg);
  font-size: 12px;
  line-height: 1.4;
  box-shadow: 0 4px 14px rgb(0 0 0 / 22%);
  pointer-events: none;
  animation: nb-toast-in 0.15s ease-out;
}
@keyframes nb-toast-in {
  from {
    opacity: 0;
    transform: translateY(3px);
  }
}
/* Post-save notice: the save's own HMR reload replaces the page, so this variant is
   FIXED (survives layout, findable at the same spot on the fresh page), wider, and
   dismissible — the base toast is a 2s pointer-events-none flash. */
.nb-toast--save {
  position: fixed;
  top: calc(var(--topbar-h) + 10px);
  left: 50%;
  /* `translate`, not `transform`: the entry animation animates `transform` and would
     override the centring for its first frames. */
  translate: -50% 0;
  max-width: min(42rem, 90vw);
  z-index: 95;
  pointer-events: auto;
  cursor: pointer;
}
.nb-edit-close {
  display: flex;
  align-items: flex-start;
  gap: 0.35rem;
  cursor: pointer;
}
.nb-edit-close input {
  margin: 2px 0 0;
  flex: none;
}
.nb-edit-note {
  width: 100%;
  box-sizing: border-box;
  padding: 0.25rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  outline: none;
}
.nb-edit-note:focus {
  border-color: var(--accent);
}
/* A refusal BLOCKS the save — it cannot read like a caption. Coloured text alone was
   missed entirely on a card the eye already scanned, so it gets a surface of its own.
   Same literals as .nb-edit-act--armed above: there is no danger token, and inventing one
   here would put a colour outside the documented --nb-* surface. `.nb-edit-warn` stays
   text-only — it annotates a link inline, it is not an alert. */
.nb-edit-msg {
  margin: 0 0 0.4rem;
  padding: 0.4rem 0.55rem;
  border-radius: 5px;
  border-left: 3px solid currentColor;
  font-weight: 500;
}
.nb-edit-msg[data-kind="error"] {
  color: #c0392b;
  background: color-mix(in srgb, #c0392b 13%, transparent);
}
.nb-edit-msg[data-kind="warn"] {
  color: #b9770e;
  background: color-mix(in srgb, #b9770e 15%, transparent);
}
.nb-edit-warn {
  color: #b9770e;
}
.nb-edit-extend {
  font: inherit;
  font-size: 11.5px;
  padding: 0.2rem 0.5rem;
  color: var(--accent);
  background: none;
  border: 1px solid var(--accent);
  border-radius: 5px;
  cursor: pointer;
}

/* Tooltip for the editor chrome. The native `title` is too slow, unstyleable and lands
   wherever the OS wants — unusable for 30px buttons in a narrow gutter. */
.nb-edit-tip {
  position: absolute;
  z-index: 70;
  max-width: 15rem;
  padding: 4px 8px;
  border-radius: 6px;
  background: var(--text);
  color: var(--bg);
  font-size: 11.5px;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 4px 14px rgb(0 0 0 / 22%);
}

/* ── in-page editor, touch ────────────────────────────────────────────────
   Notion's mobile shape, under our gestures: a tap ARMS the block (the chip below), and
   once a block is open the writing tools live in ONE horizontal bar anchored to the top
   of the software keyboard — where the thumbs are. The session card keeps only its body
   and docks right above that bar; the + / Turn-into / block menus open as bottom
   sheets. Everything here is keyed on CLASSES the script sets from the pointer type,
   not on a width or pointer media query — the class is the condition (same lesson as
   .cmt-float--bar), and it keeps a touchscreen laptop honest. */

/* The keyboard bar. --nb-edit-kb mirrors the software keyboard's height
   (visualViewport) while a touch session is open; closed keyboard = safe-area only. */
.nb-edit-dockbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--nb-edit-kb, 0px);
  z-index: 96;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
  background: var(--chrome-bg);
  /* Accent-tinted top edge: the same colour that edges the live block — one signature
     for "this is editing", and the bar's strongest cue that it is interactive chrome. */
  border-top: 1px solid color-mix(in srgb, var(--accent) 50%, var(--chrome-border));
  /* Solid bleed below the bar: sub-pixel rounding between the bar's bottom and the
     keyboard's top edge otherwise shows a hairline of page (seen on device). */
  box-shadow: 0 2px 0 0 var(--chrome-bg);
}
.nb-edit-dock-scroll {
  display: flex;
  align-items: center;
  gap: 2px;
  flex: 1;
  overflow-x: auto;
  scrollbar-width: none;
}
/* Session exits (✕ / ✓), grouped after a light divider: left of it you act on the
   content, right of it you end the session. */
.nb-edit-dock-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
  margin-left: 4px;
  padding-left: 8px;
  border-left: 1px solid var(--chrome-border);
}
.nb-edit-dock-scroll::-webkit-scrollbar {
  display: none;
}
.nb-edit-dock-btn {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 44px;
  height: 44px;
  padding: 0 10px;
  font: inherit;
  font-size: 14px;
  color: var(--text);
  background: none;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  touch-action: manipulation;
}
.nb-edit-dock-btn svg {
  width: 18px;
  height: 18px;
}
.nb-edit-dock-btn.is-active {
  color: var(--accent);
  background: var(--accent-soft);
}
.nb-edit-dock-btn--done {
  color: var(--accent);
  font-weight: 600;
}
/* The session card, docked just above the bar. Its action row is the bar's job on
   touch; only the body (journal, closures, messages) shows — and the whole card stays
   out of the way until that body has something to say. */
.nb-edit-card--dock {
  position: fixed;
  top: auto;
  left: 10px;
  right: 10px;
  bottom: calc(var(--nb-edit-kb, 0px) + env(safe-area-inset-bottom, 0px) + 58px);
  margin: 0;
  z-index: 94;
  max-height: min(38vh, 320px);
  overflow-y: auto;
  font-size: 13px;
  box-shadow: 0 -6px 24px rgb(0 0 0 / 18%);
}
.nb-edit-card--dock .nb-edit-card-actions {
  display: none;
}
.nb-edit-card--dock .nb-edit-card-body {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
.nb-edit-card--dock:not(:has(.nb-edit-card-body:not([hidden]))) {
  display: none;
}
.nb-edit-card--dock .nb-edit-note,
.nb-edit-card--dock .nb-edit-close {
  min-height: 40px;
}
.nb-edit-card--dock .nb-edit-note {
  font-size: 16px; /* avoid iOS focus-zoom */
}
/* Raw-Markdown surface on touch: same zoom rule as every mobile input. */
.nb-edit-live--touch .nb-edit-source {
  font-size: 16px;
}
/* The + / Turn-into sheets, above the bar. Same list language as the desktop menus. */
.nb-edit-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--nb-edit-kb, 0px) + env(safe-area-inset-bottom, 0px) + 58px);
  z-index: 97;
  display: flex;
  flex-direction: column;
  max-height: min(50vh, 420px);
  padding: 6px;
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -8px 30px rgb(0 0 0 / 25%);
}
.nb-edit-sheet-title {
  padding: 8px 12px 4px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.nb-edit-sheet-list {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.nb-edit-sheet .nb-edit-menu-item,
.nb-edit-sheet .nb-block-menu-item {
  min-height: 46px;
  font-size: 15px;
}
/* The ⋮ block menu as a bottom sheet (no hover, no ⋮⋮ handle to hang a popover off). */
.nb-block-menu--sheet {
  top: auto;
  left: 0;
  right: 0;
  bottom: 0;
  position: fixed;
  min-width: 0;
  padding: 8px 8px calc(10px + env(safe-area-inset-bottom, 0px));
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -8px 30px rgb(0 0 0 / 25%);
  z-index: 97;
}
.nb-block-menu--sheet .nb-block-menu-item {
  min-height: 48px;
  font-size: 15px;
}
/* Tapped, not yet editing: a tap must never be destructive, so it only ARMS the block
   and raises the chip. The outline says which block the chip refers to. (The attribute
   and these classes are only ever set on touch — no media query needed.) */
[data-nb-armed] {
  outline: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
  outline-offset: 3px;
  border-radius: 4px;
}
.nb-edit-chipbar {
  position: fixed;
  top: calc(var(--topbar-h) + 8px);
  left: 12px;
  right: 12px;
  z-index: 95;
  display: flex;
  gap: 8px;
}
.nb-edit-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex: 1;
  min-height: 46px;
  padding: 0 14px;
  font: inherit;
  font-size: 15px;
  color: var(--bg);
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 25%);
  cursor: pointer;
}
.nb-edit-chip svg {
  width: 16px;
  height: 16px;
}
.nb-edit-chip--menu {
  flex: 0 0 46px;
  padding: 0;
  color: var(--text);
  background: var(--chrome-bg);
  border: 1px solid var(--chrome-border);
}
@media (pointer: coarse) {
  .nb-edit-handle {
    display: none;
  }
  .nb-edit-tip {
    display: none;
  }
  /* The block being edited must stay clear of the topbar chip zone and visible above
     the keyboard bar; the browser scrolls the focused editor into this margin. */
  .nb-edit-live {
    scroll-margin-top: 30vh;
    scroll-margin-bottom: 40vh;
  }
}

/* Selection affordance as a docked bottom action-bar. The `--bar` class is added by JS
   from the POINTER TYPE (touch), so its styling must not hang off a WIDTH media query —
   it used to live inside `max-width: 1024px`, which meant that on a touch device wider
   than that (tablet in landscape, touchscreen laptop) the class was applied with nothing
   to position it: the button left the screen and commenting became impossible. The class
   is the condition; the width is not. */
.cmt-float.cmt-float--bar {
  /* TOP, not bottom. The bottom of a phone screen is not ours: Android stacks its own
     furniture there (the "tap to search" chip, the gesture pill) and it covered this
     button outright — the action was simply unreachable. iOS is no better once the
     keyboard is up. The top edge, just under our own sticky topbar, is the one strip
     no platform claims, and it is clear of the native selection callout too (which is
     drawn next to the selection, mid-screen). */
  top: calc(var(--topbar-h) + 8px) !important;
  bottom: auto;
  left: 12px !important;
  right: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: auto;
  min-height: 44px;
  font-size: 15px;
  border-radius: 10px;
  z-index: 90;
}

} /* @layer nb-base */
