/* Chrome for every worker-rendered page.
   Lives in a static file rather than an inline <style> block so the
   Content-Security-Policy can keep `style-src 'self'` with no
   'unsafe-inline' — the same directive that keeps a stored-injection
   payload from executing. Anything that used to be a style="..."
   attribute in lib/auth.js or lib/kb.js is a class in here now, for
   the same reason: CSP blocks inline style attributes too.

   TWO SHELLS SHARE THIS ONE FILE, and each is namespaced under its own
   <body> class:

     body.auth  lib/html.js page()        — narrow 460px card, auth forms only
     body.kb    lib/kb-layout.js kbLayout() — full-width intelligence report

   The namespacing is load-bearing, not tidiness. Both shells want a rule
   called `.card`: in the auth shell it is the 460px login panel, in the KB
   shell it is a grid tile with a pointer cursor. Same for h1/h2/input/
   select/button/table/th/td. Un-namespaced they would overwrite each other
   in source order and one of the two shells would silently be wrong. Add new
   rules INSIDE one of the two blocks below unless they are genuinely shared
   (the tokens, .note, and the editor surface are the only shared things).

   The KB block is the house style for every intelligence-report surface, so
   it is written generically (.site-header/.wrap/.page-head/.controls/.grid/
   .card/.cmp-table/.doc) rather than per-route — a new report page adopts it
   by rendering through kbLayout().

   index.html (the legacy static competitor site at /) LINKS THIS FILE and
   carries <body class="kb"> too, so the two surfaces share one header, one
   palette and one set of chrome rules instead of drifting apart. Its inline
   <style> is now only the rules unique to that page (the strategy CTA, its
   own tag colours, the list/detail toggle). */

/* ==========================================================================
   Design tokens — shared. Verbatim from the :root block in index.html so the
   two surfaces cannot drift apart.
   ========================================================================== */
:root {
  --bg: #F2ECDD;
  --card: #FBF7EC;
  --inset: #EAE3D1;
  --border: #D8CEB8;
  --border2: #C2B698;
  --field: #FFFDF6;
  --ink: #2A2118;
  --ink2: #6B5D49;
  --ink3: #98876C;
  --navy: #09476F;
  --navy-ink: #0A3653;
  --canary: #FFD91C;
  --emerald: #02894B;
  --emerald-bg: #E1F1E7;
  --emerald-ink: #0B4A2C;
  --rust: #9E3A14;
  --rust-bg: #F7E6DE;
  --rust-ink: #6B2A10;
  --orange: #E66912;
  --orange-bg: #FBEADC;
  --orange-ink: #8A3E0B;
  --teal: #0B7C74;
  --teal-bg: #DEEDEB;
  --teal-ink: #0A4A45;
  --serif: Georgia, "Times New Roman", serif;
  --sans: "Segoe UI", system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, "Cascadia Code", Consolas, monospace;
}

/* ==========================================================================
   RALEWAY — font trial, self-hosted.
   Served from /vendor/fonts, NOT from fonts.googleapis.com: the CSP is
   `style-src 'self'; font-src 'self'` with no external origin anywhere, which
   is the same reason TipTap is vendored. One variable woff2 covers the whole
   weight range, so the trial costs 48KB and one request.
   Switched on per page by <body class="kb font-trial"> — see the "Try Raleway"
   toggle in the dossier meta bar, which flips the same page between the two
   pairings so they can be compared on identical content.
   ========================================================================== */

@font-face {
  font-family: "Raleway";
  src: url("/vendor/fonts/raleway-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

.kb.font-trial {
  --serif: "Raleway", Georgia, serif;
  --sans: "Raleway", "Segoe UI", system-ui, sans-serif;
}

/* ==========================================================================
   AUTH SHELL — /login, /setup, /signup, /users, /password.
   Deliberately unchanged: these are intentionally simple forms. Every rule
   below is the pre-existing one, prefixed with its body class so the KB
   design system cannot reach it.
   ========================================================================== */

body.auth {
  font-family: "Segoe UI", system-ui, sans-serif;
  background: #F2ECDD;
  color: #2A2118;
  display: flex;
  justify-content: center;
  padding: 9vh 20px;
  margin: 0;
}

.auth .card {
  background: #FBF7EC;
  border: 1px solid #D8CEB8;
  border-radius: 14px;
  padding: 28px 30px;
  max-width: 460px;
  width: 100%;
}

.auth h1 {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 21px;
  margin: 0 0 14px;
}

.auth h2 {
  font-family: Georgia, serif;
  font-size: 16px;
  margin: 20px 0 8px;
}

.auth label {
  display: block;
  font-size: 13px;
  margin: 10px 0;
}

.auth input,
.auth select {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-top: 4px;
  padding: 9px 10px;
  border: 1px solid #C2B698;
  border-radius: 8px;
  font-size: 14px;
  background: #FFFDF6;
  color: #2A2118;
}

.auth button {
  margin-top: 14px;
  padding: 9px 18px;
  border: 1px solid #09476F;
  background: #09476F;
  color: #F2ECDD;
  border-radius: 8px;
  font-size: 14px;
  cursor: pointer;
}

.auth table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.auth th,
.auth td {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid #D8CEB8;
}

.auth a { color: #09476F; }

/* /users role picker and Remove button sit inside per-row forms that must
   not add vertical rhythm to the table cell. */
.inline-form { margin: 0; }

.role-form {
  margin: 0;
  display: flex;
  gap: 6px;
}

/* ==========================================================================
   SHARED — used by both shells.
   ========================================================================== */

.note {
  background: #F3E7C6;
  color: #7A5A14;
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 13px;
}

/* TipTap mounts into this div on /kb/new and /kb/edit/:slug. */
.editor-surface {
  border: 1px solid var(--border2);
  border-radius: 8px;
  min-height: 240px;
  padding: 10px;
  background: var(--field);
}

/* ==========================================================================
   KB / INTELLIGENCE-REPORT SHELL — lib/kb-layout.js kbLayout().
   Merged from the inline <style> in index.html. Full-width, no boxed
   max-width wrapper, no horizontal body scrollbar.
   ========================================================================== */

body.kb {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  margin: 0;
  padding: 0;
  line-height: 1.6;
  font-size: 15px;
  overflow-x: hidden;
}

body.kb,
body.kb * { box-sizing: border-box; }

/* ---- the persistent site header ---------------------------------------
   Full-bleed, sticky, on its own navy ground with a canary rule under it, so
   it reads as chrome rather than as floating text at the top of the page.
   The SAME markup and the same classes are hard-coded into index.html (the
   legacy static competitor site at /), because / and /kb have to look like one
   product. If you change a class name here, change it there too. */

.kb .site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--navy-ink);
  border-bottom: 3px solid var(--canary);
  box-shadow: 0 2px 12px rgba(10, 54, 83, .22);
}

.kb .site-header-inner {
  display: flex;
  align-items: stretch;
  gap: 8px;
  padding: 0 24px;
  min-height: 56px;
  flex-wrap: wrap;
}

/* ---- brand wordmark ---------------------------------------------------- */

.kb .brand {
  display: flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  font-family: var(--serif);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: .01em;
  padding-right: 20px;
  margin-right: 4px;
  border-right: 1px solid rgba(255, 255, 255, .16);
  white-space: nowrap;
}

.kb .brand-mark { color: var(--canary); }
.kb .brand-word { color: #fff; font-weight: 400; }
.kb .brand:hover .brand-word { color: var(--canary); }

/* ---- primary nav ------------------------------------------------------- */

.kb .mainnav { display: flex; align-items: stretch; flex: 1 1 auto; min-width: 0; }

.kb .mainnav-list {
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
}

.kb .nav-item { position: relative; display: flex; }

/* Real hit areas: the whole 56px-tall cell is the target, not just the text. */
.kb .nav-link {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0 14px;
  min-height: 56px;
  font-size: 13.5px;
  font-weight: 600;
  color: #CFE0EC;
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  margin-bottom: -3px; /* sit on the header's own canary rule */
}

.kb .nav-link:hover,
.kb .nav-item:focus-within > .nav-link {
  color: #fff;
  background: rgba(255, 255, 255, .09);
}

.kb .nav-link.current {
  color: #fff;
  background: rgba(0, 0, 0, .22);
  border-bottom-color: var(--canary);
}

/* The dropdown affordance. A CSS ::after rather than a character in the
   markup, so the link text stays exactly the category name. */
.kb .has-menu > .nav-link::after {
  content: "\25BE";
  font-size: 10px;
  opacity: .75;
}

/* ---- CSS-only dropdown -------------------------------------------------
   No JavaScript anywhere in this menu. `script-src 'self'` means behaviour
   has to load from a separate file before it works, and a nav that needs that
   file to expose half the site fails badly when it does not arrive.
   :hover opens it for a mouse, :focus-within opens it for the keyboard, and
   the parent link still navigates to the category's own listing, so touch
   users are never stranded. */

.kb .submenu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 230px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: var(--card);
  border: 1px solid var(--border2);
  border-top: 3px solid var(--canary);
  border-radius: 0 0 10px 10px;
  box-shadow: 0 10px 24px rgba(10, 54, 83, .22);
  z-index: 60;
}

.kb .nav-item.has-menu:hover > .submenu,
.kb .nav-item.has-menu:focus-within > .submenu { display: block; }

.kb .submenu-link {
  display: block;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--navy-ink);
  text-decoration: none;
  white-space: nowrap;
}

.kb .submenu-link:hover { background: var(--inset); color: var(--navy); }

.kb .submenu-link.current {
  background: var(--navy);
  color: #fff;
  font-weight: 600;
}

/* ---- account area — right-aligned, smaller, secondary ------------------ */

.kb .header-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-left: auto;
  padding-left: 18px;
  border-left: 1px solid rgba(255, 255, 255, .16);
}

.kb .header-link {
  font-family: var(--mono);
  font-size: 11.5px;
  color: #9FBACC;
  text-decoration: none;
  white-space: nowrap;
}

.kb .header-link:hover { color: var(--canary); text-decoration: underline; }

.kb .btn-new {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 6px 12px;
  border: 1px solid var(--canary);
  border-radius: 999px;
  color: var(--canary);
  text-decoration: none;
  white-space: nowrap;
}

.kb .btn-new:hover,
.kb .btn-new.current { background: var(--canary); color: var(--navy-ink); }

/* ---- content region ---------------------------------------------------- */

.kb .wrap { max-width: none; margin: 0; padding: 26px 32px 64px; }

/* ---- page title area — breadcrumbs, h1, standfirst ---------------------- */

.kb .page-head { margin: 0 0 22px; }

.kb .crumbs { margin: 0 0 10px; }

.kb .crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--mono);
  font-size: 11.5px;
}

.kb .crumbs li { display: flex; align-items: center; gap: 6px; }

.kb .crumbs li + li::before {
  content: "/";
  color: var(--ink3);
}

.kb .crumbs a { color: var(--navy); text-decoration: none; }
.kb .crumbs a:hover { text-decoration: underline; }
.kb .crumbs [aria-current="page"] { color: var(--ink2); }

.kb .page-title,
.kb h1 {
  font-family: var(--serif);
  font-size: 30px;
  font-weight: 700;
  margin: 0;
  color: var(--navy-ink);
}

.kb .standfirst { color: var(--ink2); margin: 8px 0 0; max-width: 78ch; }

/* ---- in-section sub-nav (a parent category's children) ------------------ */

.kb .subnav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 18px;
  padding: 10px 14px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.kb .subnav-label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink3);
  margin-right: 4px;
}

.kb .subnav-link {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--navy);
  text-decoration: none;
  padding: 4px 10px;
  border: 1px solid var(--border2);
  border-radius: 999px;
  background: var(--field);
}

.kb .subnav-link:hover { border-color: var(--canary); background: var(--canary); color: var(--navy-ink); }

/* ---- controls (filter + view toggle) ---------------------------------- */

.kb .controls {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 20px;
}

.kb .controls input[type="search"],
.kb .controls input[type="text"] {
  flex: 1;
  min-width: 200px;
  padding: 9px 12px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  font-size: 14px;
  background: var(--card);
  color: var(--ink);
  font-family: var(--sans);
}

.kb .controls select {
  padding: 9px 10px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  font-size: 13px;
  background: var(--card);
  color: var(--ink);
}

.kb .controls button {
  font-family: var(--sans);
  font-size: 13px;
  padding: 9px 16px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #fff;
  border-radius: 8px;
  cursor: pointer;
}

.kb .count {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink3);
  white-space: nowrap;
}

.kb .view-toggle { display: flex; gap: 4px; }

/* Links, not buttons: the view is a URL like everything else about the view. */
.kb .view-toggle a {
  font-family: var(--mono);
  font-size: 11px;
  padding: 6px 12px;
  border: 1px solid var(--border2);
  background: var(--card);
  color: var(--ink2);
  border-radius: 6px;
  text-decoration: none;
}

.kb .view-toggle a:hover { border-color: var(--canary); }
.kb .view-toggle a.active { background: var(--navy); color: #fff; border-color: var(--navy); }

.kb .per-label {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink3);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 6px;
}

.kb .per-select { padding: 7px 8px; }

/* ---- pagination --------------------------------------------------------
   Prev / page indicator / next, plus the range readout. The readout is the
   point: "Showing 51-100 of 107" tells you the size of what you are looking
   at, which a bare page number never does. Every control is a link carrying
   the entire view, so a paged, sorted, filtered listing is one shareable URL. */

.kb .pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin: 12px 0;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink3);
}

.kb .pager-controls { display: flex; align-items: center; gap: 6px; }

.kb .pager-step {
  padding: 5px 12px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  background: var(--card);
  color: var(--navy);
  text-decoration: none;
}

.kb .pager-step:hover { background: var(--canary); border-color: var(--canary); color: var(--navy-ink); }
.kb .pager-step.disabled { color: var(--ink3); opacity: .45; background: var(--inset); }
.kb .pager-at { padding: 0 6px; color: var(--ink2); }

/* ---- card grid --------------------------------------------------------- */

.kb .grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}

.kb .grid.off { display: none; }

.kb .card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px;
  cursor: pointer;
  text-decoration: none;
  color: inherit;
  display: block;
  transition: border-color .12s;
}

.kb .card:hover { border-color: var(--canary); }
.kb .card h3 { font-family: var(--serif); font-size: 17px; margin: 0 0 6px; color: var(--navy-ink); }

.kb .card p {
  font-size: 13px;
  color: var(--ink2);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A category tile holds links to its children, so it cannot itself be an <a>. */
.kb .card.cat-card { cursor: default; }
.kb .card.cat-card h3 { margin: 0 0 4px; }
.kb .card.cat-card h3 a { color: var(--navy-ink); text-decoration: none; }
.kb .card.cat-card:hover h3 a { color: var(--navy); }

.kb .card.hidden { display: none; }

.kb .tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }

.kb .tag {
  font-family: var(--mono);
  font-size: 10px;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--inset);
  color: var(--ink3);
  text-decoration: none;
}

.kb a.tag:hover { background: var(--canary); color: var(--navy-ink); }
.kb .tag.category { background: var(--emerald-bg); color: var(--emerald-ink); font-weight: 600; }
.kb .tag.draft { background: var(--orange-bg); color: var(--orange-ink); font-weight: 600; }
.kb .tag.archived { background: var(--rust-bg); color: var(--rust-ink); font-weight: 600; }
.kb .tag.child { background: var(--teal-bg); color: var(--teal-ink); font-weight: 600; }

.kb .empty {
  background: var(--card);
  border: 1px dashed var(--border2);
  border-radius: 12px;
  padding: 26px;
  color: var(--ink2);
  text-align: center;
  font-family: var(--mono);
  font-size: 13px;
}

/* ---- sortable table view ---------------------------------------------- */

/* The scroll container is what makes a 107-row, ten-column table readable.
   It scrolls in BOTH axes inside its own box — never the page body — and the
   header row stays pinned to the top of it, so you always know which column
   you are reading. Sticky only works against a scrollport, which is why the
   max-height is here and not decorative. */
.kb .table-wrap {
  overflow: auto;
  max-height: calc(100vh - 260px);
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
}

.kb .cmp-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.kb .cmp-table th {
  text-align: left;
  font-family: var(--mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink3);
  background: var(--inset);
  padding: 9px 12px;
  white-space: nowrap;
  user-select: none;
  /* Pinned to the top of the scroll container below, so a 107-row table never
     loses its column labels. Sticky only engages against a scrollport, which
     is why .table-wrap has a max-height. */
  position: sticky;
  top: 0;
  z-index: 2;
  box-shadow: inset 0 -1px 0 var(--border2);
}

/* The whole header cell is the sort control, and it is an <a>, not a click
   handler: the sort belongs in the URL so the view can be linked, and a link
   is the only control that still works with JavaScript off. The negative
   margin cancels the cell's padding and the link puts it back, so the hit
   area fills the cell while a table that renders PLAIN header text — the
   legacy competitor table in index.html shares these rules — keeps its
   padding. */
.kb .cmp-table th > a {
  display: block;
  margin: -9px -12px;
  padding: 9px 12px;
  color: inherit;
  text-decoration: none;
  font-weight: inherit;
}

.kb .cmp-table th > a:hover { color: var(--navy); background: var(--border); }
.kb .cmp-table th.sorted { color: var(--navy-ink); background: var(--canary); }
.kb .cmp-table th.sorted > a::after { content: " \25BE"; }
.kb .cmp-table th.sorted.asc > a::after { content: " \25B4"; }
.kb .cmp-table th.col-num > a { text-align: right; }

/* Numbers align right so digits line up down the page — the only reason a
   numeric column beats a prose one is that you can compare it at a glance. */
.kb .cmp-table td.col-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.kb .cmp-table td.col-title { font-weight: 600; min-width: 16ch; }
.kb .cmp-table td.col-wide { min-width: 26ch; }
.kb .cmp-table td a { overflow-wrap: anywhere; }

.kb .cmp-table td {
  padding: 9px 12px;
  border-top: 1px solid var(--border);
  vertical-align: top;
  overflow-wrap: break-word;
}

.kb .cmp-table tr:hover td { background: var(--inset); }
.kb .cmp-table tr.hidden { display: none; }
.kb .cmp-table a { color: var(--navy-ink); font-weight: 600; text-decoration: none; }
.kb .cmp-table a:hover { text-decoration: underline; }
.kb .cmp-table .muted { color: var(--ink3); }

.kb .cmp-table .m-tags { display: flex; gap: 4px; flex-wrap: wrap; }

/* ---- document view (a KB page body renders exactly like a dossier) ----- */

.kb .back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--navy);
  text-decoration: none;
  margin-bottom: 18px;
  cursor: pointer;
}

.kb .back:hover { text-decoration: underline; }

.kb .doc {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 34px 38px;
}

/* A readable measure for long-form prose. The CARD stays full width — wide
   tables, code blocks and images need the room, and boxing the whole page
   would fight the fluid layout — but running body copy across a 1400px
   monitor is unreadable, so every direct text child of .doc is capped at a
   normal column and the wide elements below opt back out. */
.kb .doc > * { max-width: 74ch; }
.kb .doc.entity > .facts,
.kb .doc.entity > .doc-media,
.kb .doc.entity > .doc-body { max-width: none; }

.kb .doc > table,
.kb .doc > pre,
.kb .doc > hr,
.kb .doc > .table-wrap { max-width: none; }

/* An <h1> INSIDE a document body, which is a heading in the prose — not the
   page title. The page title is .page-title up in .page-head and is the only
   h1 the shell itself renders. */
.kb .doc h1 { font-size: 26px; margin: 26px 0 10px; }
.kb .doc > h1:first-child { margin-top: 0; }

.kb .doc h2 {
  font-family: var(--serif);
  font-size: 19px;
  color: var(--navy-ink);
  margin: 32px 0 12px;
  border-bottom: 2px solid var(--emerald);
  padding-bottom: 7px;
}

.kb .doc h2:first-of-type { margin-top: 0; }
.kb .doc h3 { font-family: var(--serif); font-size: 16.5px; color: var(--rust-ink); margin: 20px 0 8px; }
.kb .doc h4, .kb .doc h5, .kb .doc h6 { font-family: var(--serif); font-size: 15px; color: var(--ink2); margin: 16px 0 6px; }
.kb .doc p { margin: 0 0 11px; }
.kb .doc ul, .kb .doc ol { margin: 0 0 11px; padding-left: 20px; }
.kb .doc li { margin: 5px 0; }

.kb .doc blockquote {
  margin: 14px 0;
  padding: 12px 18px;
  background: var(--emerald-bg);
  border-left: 4px solid var(--emerald);
  border-radius: 0 8px 8px 0;
  color: var(--ink);
}

.kb .doc blockquote p:last-child { margin-bottom: 0; }

.kb .doc table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
  margin: 14px 0 20px;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 0 0 1px var(--border2);
}

.kb .doc th {
  text-align: left;
  font-family: var(--mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink3);
  background: var(--inset);
  padding: 8px 10px;
}

.kb .doc td { padding: 9px 10px; border-top: 1px solid var(--border); vertical-align: top; background: var(--card); }
.kb .doc tr:nth-child(even) td { background: var(--inset); }
.kb .doc hr { border: none; border-top: 1px solid var(--border); margin: 26px 0; }
.kb .doc a { color: var(--navy); }

.kb .doc code {
  font-family: var(--mono);
  font-size: 12.5px;
  background: var(--inset);
  padding: 1px 5px;
  border-radius: 4px;
}

.kb .doc pre {
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 12.5px;
}

.kb .doc pre code { background: none; padding: 0; }

.kb .doc-meta {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink3);
  margin-bottom: 22px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.kb .doc-meta a { color: var(--navy); }

/* ---- entity detail: the facts block ------------------------------------
   Everything page_meta holds for this page, above the narrative. This is
   where the keys that were too long or too sparse to earn a table column
   live, which is why capping the table's columns loses nothing. */

.kb .facts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0;
  margin: 0 0 26px;
  padding: 4px 0 0;
  border-top: 2px solid var(--navy-ink);
  max-width: none;
}

.kb .fact {
  padding: 10px 16px 10px 0;
  border-bottom: 1px solid var(--border);
  min-width: 0;
}

.kb .facts dt {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink3);
  margin: 0 0 3px;
}

.kb .facts dd {
  margin: 0;
  font-size: 14px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.kb .facts dd a { color: var(--navy); }
.kb .facts .muted { color: var(--ink3); }

/* The narrative below the facts keeps the reading measure; the facts grid
   above deliberately does not, because it is a table of contents, not prose. */
.kb .doc-body > * { max-width: 74ch; }
.kb .doc-body > table,
.kb .doc-body > pre,
.kb .doc-body > hr { max-width: none; }

/* ATTACHMENTS. Screenshots of ad creatives, R2-backed — lib/attachments.js's
   attachmentsSection(). Sits between the facts and the narrative, full width,
   via the .doc.entity > .doc-media max-width:none override above; nothing
   around it assumes the body is the last child. */
.kb .doc-media {
  margin: 0 0 26px;
  max-width: none;
}

.kb .media-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 10px;
}

.kb .media-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink3);
}

.kb .media-count {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink3);
}

.kb .media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 12px;
  margin: 0 0 16px;
}

.kb .media-item {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.kb .media-thumb {
  display: block;
  background: var(--inset);
}

.kb .media-thumb img {
  width: 100%;
  height: 140px;
  object-fit: cover;
  display: block;
}

.kb .media-caption {
  padding: 8px 10px 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.kb .media-filename {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.kb .media-meta {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink3);
}

.kb .media-delete-form { margin: 4px 0 0; }

.kb .media-delete-btn {
  font-family: var(--mono);
  font-size: 10.5px;
  padding: 4px 10px;
  border: 1px solid var(--rust);
  background: var(--rust-bg);
  color: var(--rust-ink);
  border-radius: 6px;
  cursor: pointer;
}

.kb .media-delete-btn:hover { background: var(--rust); color: #fff; }

.kb .media-empty {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink3);
  margin: 0 0 14px;
}

.kb .media-upload {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 14px;
  border: 1px dashed var(--border2);
  border-radius: 10px;
  background: var(--inset);
  max-width: 74ch;
}

.kb .media-upload-label {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink3);
}

.kb .media-upload input[type="file"] {
  display: block;
  margin-top: 6px;
  font-size: 12.5px;
  color: var(--ink);
}

.kb .media-upload button {
  font-family: var(--sans);
  font-size: 13px;
  padding: 8px 16px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #fff;
  border-radius: 8px;
  cursor: pointer;
}

.kb .media-upload button:hover { border-color: var(--canary); }

.kb .media-error { margin: 0 0 10px; max-width: 74ch; }

.kb .font-toggle {
  font-weight: 700;
  border-bottom: 2px solid var(--canary);
  text-decoration: none;
}

/* ---- editor / long forms ---------------------------------------------- */

.kb .form-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 28px 32px;
}

.kb .form-card label {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink3);
  margin: 18px 0 0;
}

.kb .form-card label:first-of-type { margin-top: 0; }

.kb .form-card input,
.kb .form-card select {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 9px 12px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  font-size: 14px;
  font-family: var(--sans);
  background: var(--field);
  color: var(--ink);
}

.kb .form-card .editor-surface { margin-top: 6px; }

.kb .form-card button {
  margin-top: 20px;
  padding: 10px 22px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #F2ECDD;
  border-radius: 8px;
  font-size: 14px;
  font-family: var(--sans);
  cursor: pointer;
}

.kb .form-card button:hover { border-color: var(--canary); }

/* ---- arrange mode (/kb/category/:slug?arrange=1) ------------------------
   The "Arrange columns" link in the controls bar (extraLink, kb-layout.js)
   and arrange mode itself: THE REAL TABLE, in place — real <th>s, real
   widths, real data, every row — put into a mode where headers can be
   dragged left/right, hidden into a tray, relabelled, and saved. This
   replaced a separate admin page that mirrored the table as a strip of
   uniform cards; the product owner's complaint was exactly that it didn't
   look like the table, so this arranges the table itself. Drag is Pointer
   Events in /kb-columns.js; the Left/Right buttons on each header and the
   Show/hide checkboxes are the real no-JS/keyboard/assistive-tech fallback
   for the same moves, not decoration. See the report. ------------------- */

.kb .col-admin-link {
  display: inline-flex;
  align-items: center;
  padding: 8px 14px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  background: var(--card);
  color: var(--navy-ink);
  font-size: 13px;
  text-decoration: none;
}

.kb .col-admin-link:hover { border-color: var(--canary); background: var(--canary); }

.kb .arrange-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  margin: 0 0 14px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--inset);
}

.kb .arrange-hint { margin: 0; max-width: 72ch; font-size: 13px; color: var(--ink2); }
.kb .arrange-exit { flex: 0 0 auto; }

.kb .arrange-form { margin-bottom: 16px; }

.kb .arrange-table-wrap { margin-bottom: 4px; }

/* The arrange table reuses .cmp-table's look (same borders, same font, same
   sticky header) so it reads as the identical table the plain listing shows
   — not a lookalike. Only the header row behaves differently. */
.kb .arrange-table th.arrange-th,
.kb .arrange-table th.arrange-th-fixed {
  white-space: normal;
}

.kb .arrange-table th.arrange-th {
  padding: 8px 10px;
  cursor: default;
}

/* Fixed columns (Title, Updated) are never draggable, hideable or
   relabelable — category_columns has only ever governed the derived facts,
   never these — so they stay plain header text, same as the live listing. */
.kb .arrange-table th.arrange-th-fixed { vertical-align: middle; }

.kb .arrange-table th.arrange-th.dragging {
  box-shadow: 0 10px 22px rgba(0, 0, 0, .25);
  background: var(--canary);
  color: var(--navy-ink);
  z-index: 6;
  cursor: grabbing;
}

/* Drop indicator: a thin canary edge on the header the dragged one would
   land next to, updated live on pointermove — the DOM itself doesn't
   reorder (th OR its column's cells) until release. */
.kb .arrange-table th.arrange-th.drop-before { box-shadow: inset 3px 0 0 var(--canary); }
.kb .arrange-table th.arrange-th.drop-after { box-shadow: inset -3px 0 0 var(--canary); }

.kb .col-drag-handle {
  display: inline-block;
  padding: 2px 5px;
  margin-right: 4px;
  border-radius: 4px;
  font-size: 12px;
  letter-spacing: 1px;
  line-height: 1;
  color: var(--ink3);
  cursor: grab;
  user-select: none;
  vertical-align: middle;
  /* Stops the browser's own touch-scroll gesture from fighting the drag on
     a touchscreen — Pointer Events still fire, but without this the page
     tries to pan sideways at the same time. */
  touch-action: none;
}

.kb .col-drag-handle:active { cursor: grabbing; }

.kb .arrange-label-input {
  display: block;
  width: 100%;
  min-width: 9ch;
  margin: 4px 0;
  padding: 4px 6px;
  border: 1px solid transparent;
  border-radius: 5px;
  font-size: 12px;
  font-family: var(--sans);
  text-transform: none;
  letter-spacing: normal;
  background: transparent;
  color: var(--ink);
}

/* Looks like plain header text until you click in — the affordance for
   "click a header to edit its label" IS the input itself, no separate
   edit-mode toggle needed. */
.kb .arrange-label-input:hover { border-color: var(--border2); background: var(--field); }
.kb .arrange-label-input:focus { border-color: var(--canary); background: var(--field); outline: none; }

.kb .arrange-hide-toggle,
.kb .arrange-show-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}

.kb .arrange-hide-toggle input,
.kb .arrange-show-toggle input { cursor: pointer; }

.kb .arrange-hide-toggle span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  font-size: 13px;
  line-height: 1;
  color: var(--ink3);
}

.kb .arrange-hide-toggle:hover span { background: var(--rust-bg); color: var(--rust-ink); }

.kb .arrange-move-btns { display: inline-flex; gap: 4px; margin-left: 4px; }

.kb .arrange-move-btns button {
  padding: 2px 6px;
  border: 1px solid var(--border2);
  border-radius: 5px;
  background: var(--card);
  color: var(--ink);
  font-size: 12px;
  cursor: pointer;
}

.kb .arrange-move-btns button:hover:not(:disabled) { border-color: var(--canary); background: var(--canary); }
.kb .arrange-move-btns button:disabled { opacity: .35; cursor: not-allowed; }

/* ---- hidden-columns tray -------------------------------------------------
   Beneath the table, not a second table: a wrapping row of chips, each one
   a column that ISN'T currently in the live view — unticked, or never shown
   yet (including a brand-new key added ahead of the data). Dragging a chip
   onto the table (or pressing Show) restores the column with its real data;
   the small sample inside the chip is what a no-JS admin sees instead. ---- */
.kb .arrange-tray {
  border: 1px dashed var(--border2);
  border-radius: 12px;
  padding: 14px 16px 16px;
  margin-bottom: 18px;
  background: var(--inset);
}

.kb .arrange-tray-title { margin: 0 0 4px; font-size: 14px; color: var(--navy-ink); }
.kb .arrange-tray-hint { margin: 0 0 12px; font-size: 12px; color: var(--ink2); }
.kb .arrange-tray-empty { margin: 0; font-size: 12.5px; color: var(--ink3); font-style: italic; }

.kb .arrange-tray-list {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px;
}

.kb .arrange-chip {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  width: 190px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  overflow: hidden;
  opacity: .8;
}

.kb .arrange-chip.dragging {
  box-shadow: 0 10px 22px rgba(0, 0, 0, .2);
  border-color: var(--canary);
  opacity: 1;
  z-index: 5;
  cursor: grabbing;
}

.kb .arrange-chip.drop-before { box-shadow: inset 3px 0 0 var(--canary); }
.kb .arrange-chip.drop-after { box-shadow: inset -3px 0 0 var(--canary); }

.kb .arrange-chip .col-drag-handle {
  display: block;
  text-align: center;
  padding: 5px 0 3px;
  margin: 0;
  letter-spacing: 3px;
  background: var(--inset);
}

.kb .arrange-chip-body {
  padding: 2px 10px 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-bottom: 1px solid var(--border);
}

.kb .arrange-chip .arrange-label-input { border-color: var(--border2); background: var(--field); }
.kb .arrange-chip .arrange-show-toggle { padding: 6px 10px; font-size: 12px; color: var(--ink2); }

.kb .col-card-key {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink3);
  overflow-wrap: anywhere;
}

.kb .col-card-samples {
  padding: 10px 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 12.5px;
}

.kb .col-sample-row {
  padding: 6px 8px;
  background: var(--inset);
  border-radius: 6px;
  overflow-wrap: anywhere;
}

.kb .col-sample-row a { color: var(--navy-ink); font-weight: 600; text-decoration: none; }
.kb .col-sample-row a:hover { text-decoration: underline; }
.kb .col-sample-row .muted { color: var(--ink3); }

/* Placeholder/synthetic sample values — never real data — are marked TWICE:
   italic + dashed border here, and the .sample-badge chip inline with the
   value. Either alone could be missed; both together is the point ("cannot
   be mistaken for real data"). */
.kb .col-sample-row.is-placeholder {
  font-style: italic;
  color: var(--ink2);
  background: transparent;
  border: 1px dashed var(--border2);
}

.kb .sample-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--canary);
  color: var(--navy-ink);
  font-family: var(--mono);
  font-size: 9px;
  font-style: normal;
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: middle;
}

.kb .col-admin-actions { display: flex; align-items: center; gap: 12px; }

.kb .col-save-btn {
  padding: 10px 22px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #F2ECDD;
  border-radius: 8px;
  font-size: 14px;
  font-family: var(--sans);
  cursor: pointer;
}

.kb .col-save-btn:hover { border-color: var(--canary); }

/* Discard sits right next to Save — a matched pair acting on the same
   unsaved preview — but visually secondary (outline, not filled), and
   starts disabled: there is nothing to discard until something changes. */
.kb .col-discard-btn {
  padding: 10px 20px;
  border: 1px solid var(--border2);
  background: var(--card);
  color: var(--ink2);
  border-radius: 8px;
  font-size: 14px;
  font-family: var(--sans);
  cursor: pointer;
}

.kb .col-discard-btn:hover:not(:disabled) { border-color: var(--navy); color: var(--navy-ink); }
.kb .col-discard-btn:disabled { opacity: .4; cursor: not-allowed; }

.kb .col-admin-add { display: flex; align-items: center; gap: 10px; margin: 0 0 20px; }

.kb .col-admin-add label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink2); }

.kb .col-admin-add input[type="text"] {
  padding: 8px 10px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  font-size: 13px;
  font-family: var(--sans);
  background: var(--field);
  color: var(--ink);
}

.kb .col-admin-add button {
  padding: 8px 16px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #F2ECDD;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}

.kb .col-admin-add button:hover { border-color: var(--canary); }

/* Reset to automatic — its OWN bordered "danger zone", never sharing a row
   with Save/Discard. Three same-weight buttons in a line is how someone
   nukes a saved config while trying to undo a drag; this makes the
   destructive one visually unmistakable instead. */
.kb .col-admin-danger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  border: 1px solid var(--rust);
  background: var(--rust-bg);
  border-radius: 12px;
  padding: 16px 20px;
}

.kb .col-admin-danger-copy {
  margin: 0;
  max-width: 60ch;
  font-size: 12.5px;
  color: var(--rust-ink);
}

.kb .col-admin-reset { margin: 0; }

.kb .col-admin-reset button {
  padding: 9px 18px;
  border: 1px solid var(--rust);
  background: var(--rust);
  color: #FBF7EC;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}

.kb .col-admin-reset button:hover { border-color: var(--orange); background: var(--orange); }

/* ---- saved views (lib/saved-views.js) -----------------------------------
   The chip strip (one per view the viewer may see) and the "Save this
   view" <details> control, on a category listing and on search. Own views
   render first, with a Private/Shared badge; a shared view says who shared
   it. Manage controls (rename/share/delete) only render server-side for the
   owner or an admin — see canManage() in lib/saved-views.js. ------------- */

.kb .saved-views {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 14px;
}

.kb .saved-views-label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink3);
  padding-top: 8px;
}

.kb .sv-list { display: flex; flex-wrap: wrap; gap: 8px; }

.kb .sv-chip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--border2);
  border-radius: 10px;
  background: var(--card);
}

.kb .sv-link {
  font-size: 13px;
  font-weight: 600;
  color: var(--navy);
  text-decoration: none;
}

.kb .sv-link:hover { color: var(--navy-ink); text-decoration: underline; }

.kb .sv-badge {
  font-family: var(--mono);
  font-size: 10px;
  padding: 2px 7px;
  border-radius: 999px;
}

.kb .sv-badge.sv-private { background: var(--inset); color: var(--ink3); }
.kb .sv-badge.sv-shared { background: var(--teal-bg); color: var(--teal-ink); font-weight: 600; }

.kb .sv-manage {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding-left: 8px;
  border-left: 1px solid var(--border2);
}

.kb .sv-rename-input {
  width: 170px;
  padding: 5px 8px;
  border: 1px solid var(--border2);
  border-radius: 6px;
  font-size: 12px;
  font-family: var(--sans);
  background: var(--field);
  color: var(--ink);
}

.kb .sv-manage button {
  font-family: var(--mono);
  font-size: 10.5px;
  padding: 5px 9px;
  border: 1px solid var(--border2);
  border-radius: 6px;
  background: var(--card);
  color: var(--ink2);
  cursor: pointer;
}

.kb .sv-manage button:hover { border-color: var(--canary); background: var(--canary); color: var(--navy-ink); }
.kb .sv-manage button.sv-delete { border-color: var(--rust); color: var(--rust-ink); background: var(--rust-bg); }
.kb .sv-manage button.sv-delete:hover { border-color: var(--rust); background: var(--rust); color: #fff; }

.kb details.save-view {
  margin: 0 0 16px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
}

.kb details.save-view summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--navy-ink);
}

.kb .save-view-form {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
}

.kb .save-view-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--ink2);
}

.kb .save-view-form input[type="text"] {
  padding: 8px 10px;
  border: 1px solid var(--border2);
  border-radius: 8px;
  font-size: 13px;
  font-family: var(--sans);
  background: var(--field);
  color: var(--ink);
  min-width: 220px;
}

.kb .save-view-form button {
  padding: 9px 16px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #fff;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}

.kb .save-view-form button:hover { border-color: var(--canary); }

/* ---- comparison: the checkbox picker on a category listing -------------
   listing()'s selection <form> wraps the table/grid when compareBase is
   set (lib/kb-layout.js). Plain checkboxes + a GET submit, no JavaScript
   required — /kb-ui.js only adds the live "N selected" count and disables
   the button under 2, both cosmetic on top of a control that already works
   without it. -------------------------------------------------------- */

.kb .cmp-select-form { margin: 0; }

.kb .cmp-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 10px;
  padding: 10px 14px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.kb .cmp-hint {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink3);
}

.kb .cmp-submit {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  padding: 9px 16px;
  border: 1px solid var(--navy);
  background: var(--navy);
  color: #fff;
  border-radius: 8px;
  cursor: pointer;
}

.kb .cmp-submit:hover { border-color: var(--canary); background: var(--navy-ink); }
.kb .cmp-submit:disabled { opacity: .4; cursor: not-allowed; }

.kb .cmp-check-col { width: 1%; text-align: center !important; padding: 9px 8px !important; }

.kb .cmp-table input[type="checkbox"],
.kb .cmp-check-overlay input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--navy);
  cursor: pointer;
}

.kb .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* The checkbox sits OUTSIDE the card's own <a> (see gridView()'s comment) —
   this positions it as a small overlay chip in the corner instead. */
.kb .card-select { position: relative; }

.kb .cmp-check-overlay {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1;
  display: flex;
  padding: 5px;
  background: var(--card);
  border: 1px solid var(--border2);
  border-radius: 6px;
  box-shadow: 0 1px 3px rgba(42, 33, 24, .15);
}

.kb .cmp-change {
  margin: 0 0 14px;
  font-family: var(--mono);
  font-size: 12.5px;
}

.kb .cmp-change a { color: var(--navy); text-decoration: none; }
.kb .cmp-change a:hover { text-decoration: underline; }

/* ---- comparison table: attributes as rows, entities as columns ---------
   Same .cmp-table base (padding, borders, link/muted colours) as the
   listing's own table — this only overrides what the flipped orientation
   needs: a label column instead of a Title column, and a per-row rather
   than per-column highlight. ------------------------------------------- */

.kb table.cmp-compare { table-layout: fixed; }

.kb .cmp-compare .cmp-attr-label {
  text-align: left;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .03em;
  color: var(--ink2);
  background: var(--inset);
  padding: 9px 12px;
  white-space: normal;
  width: 200px;
  vertical-align: top;
}

.kb .cmp-compare thead .cmp-corner {
  color: var(--ink3);
  text-transform: uppercase;
  font-size: 10px;
}

/* The base .kb .cmp-table th rule pins EVERY <th> to the scrollport's top
   edge for the sort-header row. A comparison also uses <th scope="row"> for
   the attribute label in the body, which must NOT inherit that — otherwise
   every row label stacks at the top of the box instead of staying beside
   its own row. */
.kb .cmp-compare tbody th.cmp-attr-label {
  position: static;
  user-select: text;
}

.kb .cmp-compare .cmp-entity-col {
  text-transform: none;
  min-width: 170px;
  vertical-align: bottom;
}

.kb .cmp-compare .cmp-entity-col > a {
  display: block;
  font-family: var(--serif);
  font-size: 14.5px;
  font-weight: 700;
  color: var(--navy-ink);
  text-decoration: none;
  white-space: normal;
}

.kb .cmp-compare .cmp-entity-col > a:hover { text-decoration: underline; }

.kb .cmp-compare td { vertical-align: top; }

/* De-emphasize rows every selected entity agrees on — the differing rows are
   the ones worth the reader's attention, so a shared value recedes instead
   of competing with it for the eye. */
.kb .cmp-compare tr.cmp-same th.cmp-attr-label,
.kb .cmp-compare tr.cmp-same td { color: var(--ink3); }
.kb .cmp-compare tr.cmp-same td a { color: var(--ink3); }

/* Divergent rows get the same canary accent the sorted column header
   already uses elsewhere in this app, so "this is where the comparison
   earns its keep" reads as a signal the eye is already trained on. */
.kb .cmp-compare tr.cmp-diff th.cmp-attr-label {
  border-left: 3px solid var(--canary);
  color: var(--navy-ink);
  font-weight: 700;
}

.kb .cmp-compare tr.cmp-diff td { background: var(--field); }

.kb .cmp-empty-row {
  text-align: center;
  color: var(--ink3);
  font-family: var(--mono);
  font-size: 12.5px;
  padding: 24px;
}

.kb .site-footer {
  padding: 16px 32px 24px;
  border-top: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink3);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
}

/* ---- narrow viewports --------------------------------------------------
   The one breakpoint this app defines. The header stops being a single row:
   the brand and the account area sit on the first line, the nav wraps onto
   its own, and the dropdowns open in flow instead of floating off a bar that
   is no longer one bar. */

@media (max-width: 860px) {
  .kb .site-header { position: static; }
  .kb .site-header-inner { padding: 8px 16px; min-height: 0; }

  .kb .brand { border-right: none; padding-right: 0; }

  .kb .mainnav { order: 3; flex-basis: 100%; }
  .kb .mainnav-list { width: 100%; }

  .kb .nav-link { min-height: 40px; padding: 0 10px; }

  .kb .header-actions { border-left: none; padding-left: 0; }

  .kb .wrap { padding: 20px 16px 48px; }
  .kb .site-footer { padding: 16px; }
  .kb .doc { padding: 22px 18px; }
}

/* ---- TipTap / ProseMirror base styles ----------------------------------
   Verbatim copy of the stylesheet @tiptap/core would otherwise append to
   <head> as an inline <style data-tiptap-style> element. /editor.js passes
   injectCSS:false to stop that injection, because CSP (`style-src 'self'`,
   no 'unsafe-inline') blocks it — and a *blocked* injection is worse than a
   loud failure: the editor still mounts and looks fine, but silently loses
   white-space:pre-wrap, caret handling and the gap-cursor, so multiple
   spaces collapse and selection behaves oddly. Serving it from here keeps
   the CSP strict and the editor correct. Re-copy if TipTap is upgraded. */

.ProseMirror {
  position: relative;
}

.ProseMirror {
  word-wrap: break-word;
  white-space: pre-wrap;
  white-space: break-spaces;
  -webkit-font-variant-ligatures: none;
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
}

.ProseMirror [contenteditable="false"] {
  white-space: normal;
}

.ProseMirror [contenteditable="false"] [contenteditable="true"] {
  white-space: pre-wrap;
}

.ProseMirror pre {
  white-space: pre-wrap;
}

img.ProseMirror-separator {
  display: inline !important;
  border: none !important;
  margin: 0 !important;
  width: 0 !important;
  height: 0 !important;
}

.ProseMirror-gapcursor {
  display: none;
  pointer-events: none;
  position: absolute;
  margin: 0;
}

.ProseMirror-gapcursor:after {
  content: "";
  display: block;
  position: absolute;
  top: -2px;
  width: 20px;
  border-top: 1px solid black;
  animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
}

@keyframes ProseMirror-cursor-blink {
  to {
    visibility: hidden;
  }
}

.ProseMirror-hideselection *::selection {
  background: transparent;
}

.ProseMirror-hideselection *::-moz-selection {
  background: transparent;
}

.ProseMirror-hideselection * {
  caret-color: transparent;
}

.ProseMirror-focused .ProseMirror-gapcursor {
  display: block;
}

.tippy-box[data-animation=fade][data-state=hidden] {
  opacity: 0;
}

/* The editor's own content area fills the bordered surface. */
.editor-surface .ProseMirror {
  min-height: 200px;
  outline: none;
}
