* { box-sizing: border-box; margin: 0; padding: 0; }

/* Latin Modern (the xelatex default main font) self-hosted as woff2 so Clean
   view can render in the PDF's actual typeface — the basis of print-faithful
   Clean view. GUST Font License (OFL-compatible), vendored under /fonts. */
@font-face {
  font-family: 'Latin Modern Roman';
  src: url('/fonts/LM-regular.woff2') format('woff2');
  font-weight: normal; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Latin Modern Roman';
  src: url('/fonts/LM-italic.woff2') format('woff2');
  font-weight: normal; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Latin Modern Roman';
  src: url('/fonts/LM-bold.woff2') format('woff2');
  font-weight: bold; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Latin Modern Roman';
  src: url('/fonts/LM-bold-italic.woff2') format('woff2');
  font-weight: bold; font-style: italic; font-display: swap;
}

/* Per-selection editor fonts (Font menu) self-hosted as woff2 so the Editor
   PREVIEWS them faithfully instead of falling back to generic serif. These are
   the exact families the xelatex PDF uses (see PER_SELECTION_FONTS): EB Garamond
   (garamond), TeX Gyre Bonum (bookman), Libertinus Serif (libertine). The other
   menu fonts — Times/Arial/Palatino/Charter/Inconsolata — resolve to fonts
   shipped with the OS, so they need no @font-face. Family names match the CSS
   stacks in shared/latex-syntax.js exactly; all OFL/GUST-licensed. */
@font-face {
  font-family: 'EB Garamond';
  src: url('/fonts/Garamond-regular.woff2') format('woff2');
  font-weight: normal; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('/fonts/Garamond-italic.woff2') format('woff2');
  font-weight: normal; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('/fonts/Garamond-bold.woff2') format('woff2');
  font-weight: bold; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('/fonts/Garamond-bold-italic.woff2') format('woff2');
  font-weight: bold; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TeX Gyre Bonum';
  src: url('/fonts/Bonum-regular.woff2') format('woff2');
  font-weight: normal; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'TeX Gyre Bonum';
  src: url('/fonts/Bonum-italic.woff2') format('woff2');
  font-weight: normal; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TeX Gyre Bonum';
  src: url('/fonts/Bonum-bold.woff2') format('woff2');
  font-weight: bold; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'TeX Gyre Bonum';
  src: url('/fonts/Bonum-bold-italic.woff2') format('woff2');
  font-weight: bold; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Libertinus Serif';
  src: url('/fonts/Libertinus-regular.woff2') format('woff2');
  font-weight: normal; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Libertinus Serif';
  src: url('/fonts/Libertinus-italic.woff2') format('woff2');
  font-weight: normal; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Libertinus Serif';
  src: url('/fonts/Libertinus-bold.woff2') format('woff2');
  font-weight: bold; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Libertinus Serif';
  src: url('/fonts/Libertinus-bold-italic.woff2') format('woff2');
  font-weight: bold; font-style: italic; font-display: swap;
}

:root {
  --bg: #f3f2f2;
  --surface: #ffffff;
  --surface2: #fbfafa;
  --border: #e2e0df;
  --border-strong: #b6b1af;
  --text: #201e1d;
  --text-dim: #6f6a66;
  --accent: #940c24;
  --accent-light: #f8e6ea;
  --accent-mid: #c76179;
  --accent-600: #84091f;
  --accent-700: #6e0719;
  --code-bg: #f1efee;
  --patch-bg: #eef4ee;
  --patch-border: #6a8b6a;
  --toolbar-bg: #f3f2f2;
  --pane-label: #8a8480;
  --shadow: 0 1px 3px rgba(0,0,0,0.08);
  /* App-chrome font (menus, toolbar, modals, sidebar, status pills). The
     editor body uses a separate `--editor-font-family` so the writer's
     Format-menu choice (Charter, Bookman, etc.) is independent of the UI.
     Noto Sans matches Overleaf's chrome; the system stack covers the brief
     moment before the web font loads and the case where the user has
     blocked fonts.googleapis.com. */
  --ui-font: 'Source Serif 4', Georgia, 'Times New Roman', serif;
}

html, body { height: 100%; font-family: var(--ui-font); background: var(--bg); color: var(--text); font-size: 15px; line-height: 1.5; }

#app { display: flex; flex-direction: column; height: 100vh; position: relative; }

/* Header */
header {
  display: flex; align-items: center; gap: 12px;
  padding: 0 18px; height: 52px;
  background: var(--bg); color: var(--text);
  flex-shrink: 0;
  box-shadow: none;
  border-bottom: 2px solid var(--text);
}
/* the thin half of the thick-thin masthead rule */
header::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 54px; height: 1px; background: var(--text); pointer-events: none;
}
header h1 {
  display: flex; align-items: center;
  font-size: 17px; font-weight: 600; letter-spacing: -0.01em; color: var(--text);
}
header h1 a {
  display: flex; align-items: center; gap: 9px; color: inherit; text-decoration: none;
}
header h1 .brand-mark { height: 22px; width: auto; display: block; }
.header-spacer { flex: 1; }
.status {
  font-size: 12px; font-family: var(--ui-font);
  padding: 3px 9px; border-radius: 20px;
  background: transparent; color: var(--text-dim);
  border: 1px solid var(--border);
}
.status.connected { background: transparent; color: #2e7d4f; border-color: #cfe0d4; }

.save-status {
  font-size: 12px; font-family: var(--ui-font);
  padding: 3px 9px; margin-left: 6px; border-radius: 20px;
  background: transparent; color: var(--text-dim);
  border: 1px solid var(--border);
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.save-status.save-saved   { color: #2e7d4f; border-color: #cfe0d4; background: transparent; }
.save-status.save-saving  { color: #8a6d00; border-color: #e8dcae; background: transparent; }
.save-status.save-offline { color: var(--accent-700); border-color: var(--accent-light); background: transparent; }

/* Views */
#main { flex: 1; overflow: hidden; display: flex; }
.view { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.view.hidden { display: none; }
/* The editor view manages its own internal panes, so .view clips overflow.
   The landing (document list) view has no inner scroll region of its own, so
   it must scroll as a whole when the list is taller than the viewport. */
#landing-view { overflow-y: auto; }

/* Landing */
.landing-container { max-width: 680px; width: 100%; margin: 56px auto; padding: 0 24px; }
.landing-container h2 { font-size: 26px; font-weight: normal; margin-bottom: 28px; }
/* "How did you hear about us?", asked once at the top of the document list.
   A card rather than a full-width strip: it sits inside the 680px column with the
   documents, which reads as a question the page is asking rather than as an alert
   about something wrong. Twelve options wrap freely, so the row is never clipped
   at a laptop-height viewport the way a single-line control would be. */
/* A full-width strip along the top of the document list, in the idiom of the
   editor's own banners (.page-cap-banner): edge to edge, bottom rule, no radius.
   Question, options and Skip share ONE row, so the whole thing costs the list about
   the height of a toolbar. The chips wrap only when the window is too narrow to
   hold them, which is the one case where a second row is better than hiding an
   option behind a scroll. `flex: 0 1 auto` on the chip row lets it give up space to
   the question and Skip before it wraps, rather than pushing them off the edge. */
.heard-from-banner {
  position: relative;
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px;
  padding: 7px 76px 7px 24px;
  background: var(--accent-light); border-bottom: 1px solid var(--accent-mid);
}
.heard-from-banner.hidden { display: none; }
.heard-from-question { flex: none; font-size: 13px; line-height: 1.3; color: var(--text); }
/* Pinned to the corner rather than carried in the flex flow. As a flow item it can
   only ever land on the LAST wrapped line, so the moment the chips took two rows it
   was pushed onto a row of its own and one word cost the strip a whole line. The
   right padding above is the space it is pinned into, so no chip runs under it. */
.heard-from-skip {
  position: absolute; top: 7px; right: 24px;
  padding: 2px 4px; border: none; background: none;
  font-family: inherit; font-size: 12px; color: var(--text-dim);
  text-decoration: underline; cursor: pointer;
}
.heard-from-skip:hover { color: var(--text); }
.heard-from-options { flex: 0 1 auto; display: flex; flex-wrap: wrap; gap: 5px; }
.heard-from-option {
  padding: 3px 10px; border: 1px solid var(--accent-mid); border-radius: 999px;
  background: var(--surface); color: var(--text);
  font-family: inherit; font-size: 12px; line-height: 1.4; white-space: nowrap; cursor: pointer;
}
.heard-from-option:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); color: #fff; }
.heard-from-option:disabled { opacity: 0.55; cursor: default; }
/* Own line, so an error never reflows the row it is reporting on. */
.heard-from-msg { flex-basis: 100%; font-size: 12px; color: var(--text-dim); }
.heard-from-msg:empty { display: none; }
@media (prefers-color-scheme: dark) {
  .heard-from-banner { background: #2c1218; border-bottom-color: #7a2437; }
  .heard-from-option { background: #1e1c1b; border-color: #7a2437; color: #ede9e7; }
}

.new-doc-row { display: flex; gap: 10px; margin-bottom: 32px; }
.new-doc-row input {
  flex: 1; padding: 10px 14px; font-family: inherit; font-size: 15px;
  border: 1px solid var(--border-strong); border-radius: 4px; background: var(--surface);
}
.new-doc-row input:focus { outline: 2px solid var(--accent); border-color: var(--accent); }

button {
  padding: 8px 16px; font-family: inherit; font-size: 14px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 4px; cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
button:hover { background: var(--accent-light); border-color: var(--accent); color: var(--accent); }
button:disabled { opacity: 0.45; cursor: not-allowed; }

.doc-list { list-style: none; }
.doc-list li {
  display: flex; align-items: center; padding: 14px 16px;
  border: 1px solid var(--border); border-radius: 5px; margin-bottom: 8px;
  background: var(--surface); cursor: pointer;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.doc-list li:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.doc-list .doc-title-cell { flex: 1; font-size: 15px; }
.doc-list .doc-meta { font-size: 12px; color: var(--text-dim); margin-right: 12px; }
/* Per-file ⋯ actions menu (replaces the old inline Delete button). */
.doc-list .doc-actions { position: relative; display: flex; align-items: center; }
.doc-list .doc-menu-btn {
  padding: 2px 9px; font-size: 18px; line-height: 1; color: var(--text-dim);
  border: 1px solid transparent; background: none; border-radius: 4px; cursor: pointer;
}
.doc-list .doc-menu-btn:hover { background: var(--surface2, #faf8f5); border-color: var(--border); color: var(--text); }
.doc-menu-dropdown {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 60; min-width: 200px;
  background: var(--surface, #fff); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.15); padding: 4px; display: flex; flex-direction: column;
}
.doc-menu-dropdown.hidden { display: none; }
.doc-menu-dropdown.drop-up { top: auto; bottom: calc(100% + 4px); }
.doc-menu-item {
  text-align: left; padding: 7px 10px; font-size: 13px; background: none; border: none;
  border-radius: 4px; cursor: pointer; color: var(--text); white-space: nowrap;
}
.doc-menu-item:hover { background: var(--accent-light, #f3ece9); color: var(--accent); }
.doc-menu-item.doc-menu-danger { color: #c0392b; }
.doc-menu-item.doc-menu-danger:hover { background: #fbe9e9; color: #a3271b; }
/* "Move as appendix to…" paper picker. */
.move-paper-list { list-style: none; margin: 0; padding: 0; max-height: 46vh; overflow-y: auto; border: 1px solid var(--border); border-radius: 5px; }
.move-paper-list li { border-bottom: 1px solid var(--border); }
.move-paper-list li:last-child { border-bottom: none; }
.move-paper-item { display: block; width: 100%; text-align: left; padding: 9px 12px; background: none; border: none; cursor: pointer; font: inherit; color: var(--text); }
.move-paper-item:hover { background: var(--accent-light, #f3ece9); color: var(--accent); }
.move-paper-item:disabled { opacity: 0.5; cursor: default; }
/* Appendices render indented under their paper, with a smaller, dimmer title
   and a connector tick so the hierarchy reads at a glance. */
.doc-list li.doc-appendix { margin-left: 28px; margin-top: -4px; background: var(--surface2, #faf8f5); }
.doc-list li.doc-appendix .doc-title-cell { font-size: 13px; color: var(--text-dim); }
.doc-list li.doc-appendix .doc-title-cell::before { content: "↳ "; opacity: 0.6; }

/* Trash — a collapsed folder under the working list. Always present (a deleted
   paper needs one obvious place to be), visually quieter than the live files so
   it never competes with them for attention. */
.trash-folder { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 14px; }
.trash-header { display: flex; align-items: center; gap: 10px; }
.trash-toggle {
  flex: 1; display: flex; align-items: center; gap: 8px; padding: 7px 8px;
  background: none; border: 1px solid transparent; border-radius: 5px;
  cursor: pointer; font: inherit; font-size: 14px; color: var(--text-dim); text-align: left;
}
.trash-toggle:hover { background: var(--surface2, #faf8f5); border-color: var(--border); color: var(--text); }
.trash-caret { font-size: 11px; width: 10px; }
.trash-label { font-weight: 500; }
.trash-count {
  font-size: 12px; color: var(--text-dim); background: var(--surface2, #faf8f5);
  border: 1px solid var(--border); border-radius: 10px; padding: 0 7px; line-height: 17px;
}
.trash-count:empty { display: none; }
.trash-empty-btn {
  padding: 5px 11px; font-size: 12px; color: #c0392b; background: none;
  border: 1px solid var(--border); border-radius: 4px; cursor: pointer;
}
.trash-empty-btn:hover { background: #fbe9e9; border-color: #c0392b; }
.trash-empty-btn:disabled { opacity: 0.5; cursor: default; }
.trash-body { margin-top: 8px; }
.trash-body.hidden, .trash-empty-btn.hidden { display: none; }
/* A trashed paper does not open, so its row is not a click target. */
.trash-list li.trash-item { cursor: default; background: var(--surface2, #faf8f5); }
.trash-list li.trash-item:hover { border-color: var(--border); box-shadow: none; }
.trash-list .trash-title { color: var(--text-dim); }
/* Two buttons + a timestamp squeeze the title cell, so keep the phrases that
   must read as a unit from breaking across lines ("+ 1 appendix", the date). */
.trash-kids { font-size: 12px; opacity: 0.75; white-space: nowrap; }
.trash-list .trash-item .doc-meta { white-space: nowrap; }
.trash-item-actions { gap: 6px; flex: none; }
.trash-restore-btn, .trash-purge-btn {
  padding: 5px 11px; font-size: 12px; background: none;
  border: 1px solid var(--border); border-radius: 4px; cursor: pointer; color: var(--text);
}
.trash-restore-btn:hover { background: var(--accent-light, #f3ece9); border-color: var(--accent); color: var(--accent); }
.trash-purge-btn { color: #c0392b; }
.trash-purge-btn:hover { background: #fbe9e9; border-color: #c0392b; }
.trash-restore-btn:disabled, .trash-purge-btn:disabled { opacity: 0.5; cursor: default; }
.trash-list li.trash-empty-hint {
  border: none; background: none; cursor: default; color: var(--text-dim);
  font-size: 13px; padding: 6px 8px;
}
.trash-list li.trash-empty-hint:hover { border-color: transparent; box-shadow: none; }

/* Editor header */
.editor-header {
  display: flex; align-items: center; padding: 6px 14px; min-height: 50px;
  background: var(--bg); border-bottom: 1px solid var(--border);
  gap: 10px; flex-shrink: 0; flex-wrap: wrap;
}
.icon-btn { padding: 5px 11px; font-size: 16px; background: transparent; border-color: transparent; }
.doc-title {
  flex: 1; padding: 6px 10px; font-family: inherit; font-size: 16px;
  border: 1px solid transparent; background: transparent; border-radius: 4px;
}
.doc-title:hover { border-color: var(--border); }
.doc-title:focus { outline: 2px solid var(--accent); border-color: var(--accent); background: var(--surface); }
.editor-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; row-gap: 6px; }

/* Menu bar (Phase 1): Google-Docs order of the top-level menus, set with `order` so the
   DOM can stay put. File · Edit · View · Insert · Format · Tools, then the standalone
   controls, then Help LAST on the right.
   Help trails every control rather than sitting mid-bar: it is the one menu a writer
   opens when something has gone wrong, not while working, so it belongs out of the
   path of the controls they reach for constantly. The status pills keep the highest
   order of all — they appear and disappear as work happens, so anything ordered after
   them would shift position on its own. */
.editor-actions > .doc-menu-wrap:has(> #doc-menu-btn)    { order: 1; }
.editor-actions > .doc-menu-wrap:has(> #edit-menu-btn)   { order: 2; }
.editor-actions > .doc-menu-wrap:has(> #view-menu-btn)   { order: 3; }
.editor-actions > .doc-menu-wrap:has(> #insert-menu-btn) { order: 4; }
.editor-actions > .doc-menu-wrap:has(> #format-menu-btn) { order: 5; }
.editor-actions > .doc-menu-wrap:has(> #tools-menu-btn)  { order: 6; }
.editor-actions > .doc-menu-wrap:has(> #zoom-menu-btn)      { order: 7; }
.editor-actions > .doc-menu-wrap:has(> #header-grammar-btn) { order: 8; }
.editor-actions > #refs-btn                                { order: 9; }
.editor-actions > #agent-btn                               { order: 10; }
.editor-actions > #history-btn                             { order: 11; }
.editor-actions > .doc-menu-wrap:has(> #suggest-mode-btn)   { order: 12; }
.editor-actions > .doc-menu-wrap:has(> #help-menu-btn)      { order: 13; }
.editor-actions > #local-sync-pill,
.editor-actions > #local-tex-pill,
.editor-actions > #grammar-status,
.editor-actions > #compile-status                          { order: 14; }

/* Section headers inside a dropdown (View ▸ Mode / Zoom, Tools ▸ Repair …) */
.menu-section-label {
  padding: 7px 14px 3px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted, #8a8a8a);
}
.doc-menu .menu-item.is-active { font-weight: 600; }
.model-select {
  padding: 5px 8px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--border-strong); border-radius: 4px;
  background: var(--surface); color: var(--text); cursor: pointer;
}
.extended-thinking-label {
  display: flex; align-items: center; gap: 5px; font-size: 13px;
  color: var(--text-dim); cursor: pointer; white-space: nowrap;
}
.extended-thinking-label input { accent-color: var(--accent); }
.compile-btn {
  background: var(--accent); color: white; border-color: var(--accent);
  padding: 6px 16px; white-space: nowrap;
}
.compile-btn:hover { background: #923f30; border-color: #923f30; color: white; }
.compile-status { font-size: 12px; color: var(--text-dim); font-family: 'Courier New', monospace; white-space: nowrap; }
.compile-status.error { color: #b33; }
.compile-status.success { color: #2d7a2d; }
.compile-status.importing { font-weight: 700; color: var(--text); animation: compile-blink 1s steps(2, start) infinite; }
@keyframes compile-blink { to { visibility: hidden; } }

/* Toolbar */
.toolbar {
  display: flex; flex-direction: column; gap: 4px; padding: 6px 12px;
  background: var(--toolbar-bg); border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.toolbar-row {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.toolbar-label {
  font-size: 11px; color: var(--text-dim); text-transform: uppercase;
  letter-spacing: 0.5px; margin-right: 4px; font-family: 'Courier New', monospace;
}
.tb-btn {
  padding: 3px 9px; font-size: 13px; border-radius: 3px;
  background: transparent; border: 1px solid transparent;
  color: var(--text); cursor: pointer; font-family: var(--ui-font);
  min-width: 28px;
}
.tb-btn:hover { background: var(--accent-light); color: var(--accent); }
/* The ¶ toggle, in Word's place on the toolbar. It is the only toolbar control that holds a
   sticky ON state, so it needs a pressed look that reads at a glance — a writer with marks on
   should be able to see WHY the page is full of dots without opening a menu. */
.tb-pilcrow { font-size: 15px; line-height: 1; }
.tb-btn.is-active,
.tb-btn[aria-pressed="true"] {
  background: var(--accent-light); color: var(--accent);
  border-color: var(--accent-mid, #b8837a);
}
.tb-bold { font-weight: bold; }
.tb-italic { font-style: italic; }
.tb-under { text-decoration: underline; }
.tb-strike { text-decoration: line-through; }
.tb-divider { width: 1px; height: 20px; background: var(--border-strong); margin: 0 4px; }

/* Text-colour button: Word's "A" over a bar showing the colour a click would apply.
   The bar is a child of the glyph rather than a border on the button so it tracks the
   letter's width, not the button's padding. Its colour is set from JS. */
.tb-textcolor-glyph { position: relative; display: inline-block; font-weight: 600; line-height: 1; }
.tb-textcolor-bar {
  position: absolute; left: -1px; right: -1px; bottom: -4px; height: 3px;
  border-radius: 1px; background: #1a4fd6;
}
/* The native colour input is the OS picker (a wheel on macOS). Kept in the layout but
   visually collapsed onto its swatch, so the row reads like the preset rows above it
   and the whole label is the click target. */
.textcolor-custom { display: flex; align-items: center; cursor: pointer; }
.textcolor-custom input[type="color"] {
  width: 0; height: 0; padding: 0; margin: 0; border: 0; opacity: 0; pointer-events: none;
}

/* Toolbar dropdown menus (heading, insert) -- reuses the doc-menu aesthetic */
.tb-menu-wrap { position: relative; display: inline-block; }
.tb-menu-btn {
  padding: 3px 10px; font-size: 13px; border-radius: 3px;
  background: transparent; border: 1px solid transparent;
  color: var(--text); cursor: pointer; font-family: var(--ui-font);
  white-space: nowrap;
}
.tb-menu-btn:hover { background: var(--accent-light); color: var(--accent); }
.tb-menu {
  position: absolute; top: calc(100% + 4px); left: 0;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 5px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);
  min-width: 180px; z-index: 100;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
}
.tb-menu.hidden { display: none; }
.tb-menu-scroll { max-height: 320px; overflow-y: auto; }
.tb-menu .menu-item {
  display: block; width: 100%; text-align: left;
  padding: 8px 14px; font-size: 13px; font-family: var(--ui-font);
  background: none; border: none; border-radius: 0; cursor: pointer;
  color: var(--text);
}
.tb-menu .menu-item:hover { background: var(--accent-light); color: var(--accent); }

/* Five panes when outline is open: outline | splitter | editor | splitter | chat.
   Collapses to three when the outline is hidden. The outline splitter is
   wider than the chat splitter (14px vs 6px) so the stacked ‹/› chevrons
   that signal toggle-on-click fit comfortably. */
.editor-body {
  flex: 1; display: grid;
  grid-template-columns: var(--outline-pane-width, 220px) 14px 1fr 6px var(--chat-pane-width, 400px);
  overflow: hidden;
}
/* Collapsed state: pane shrinks to zero, but the splitter stays visible on
   the left edge so the user can click it to re-expand. Widened to 20px
   (vs 6px when expanded) so the strip is easy to spot and click. The pane
   keeps its grid slot (display:none would let auto-flow shift the splitter
   into column 1, collapsing it to its 2px borders); .pane already has
   overflow:hidden, so zero-width is enough to hide the content. */
.editor-body.outline-collapsed {
  grid-template-columns: 0 20px 1fr 6px var(--chat-pane-width, 400px);
}
.editor-body.outline-collapsed #outline-pane { border-right: none; }

/* Chat (AI) pane collapse — mirrors the outline pane but on the right edge.
   The pane shrinks to zero while its splitter widens to 20px and stays on the
   right so it can be clicked to re-expand. Four grid permutations cover the
   independent outline/chat collapse states. */
.editor-body.chat-collapsed {
  grid-template-columns: var(--outline-pane-width, 220px) 14px 1fr 20px 0;
}
.editor-body.outline-collapsed.chat-collapsed {
  grid-template-columns: 0 20px 1fr 20px 0;
}
.editor-body.chat-collapsed .chat-pane { border-left: none; }
#pane-splitter { position: relative; }
/* Stacked ›/‹ chevrons signal "click to toggle" (mirrors #outline-splitter).
   Arrows point the way the pane would move: collapse to the right. */
#pane-splitter::before {
  content: '›\A‹';
  white-space: pre;
  text-align: center;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 12px; line-height: 1.05;
  color: var(--text-dim, #888);
  pointer-events: none;
  letter-spacing: 0;
}
#pane-splitter:hover::before { color: var(--accent, #7a3b2e); }
.editor-body.chat-collapsed #pane-splitter { background: var(--surface2); }
.editor-body.chat-collapsed #pane-splitter::before { font-size: 16px; }
.editor-body.chat-collapsed #pane-splitter:hover {
  background: var(--accent-mid, rgba(122, 59, 46, 0.4));
}
/* Stacked ‹/› chevrons on the outline splitter signal "click to toggle".
   Always visible — collapsed grows them slightly to match the wider strip. */
#outline-splitter { position: relative; }
#outline-splitter::before {
  content: '‹\A›';
  white-space: pre;
  text-align: center;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 12px; line-height: 1.05;
  color: var(--text-dim, #888);
  pointer-events: none;
  letter-spacing: 0;
}
#outline-splitter:hover::before { color: var(--accent, #7a3b2e); }
.editor-body.outline-collapsed #outline-splitter { background: var(--surface2); }
.editor-body.outline-collapsed #outline-splitter::before { font-size: 16px; }
.editor-body.outline-collapsed #outline-splitter:hover {
  background: var(--accent-mid, rgba(122, 59, 46, 0.4));
}

/* ---- The name of a folded pane, set along its strip ----
   Collapsed, both panes become the same anonymous 20px column with the same pair
   of chevrons, so nothing on screen says which one a strip would reopen. The name
   is written down the strip instead, from the element's own
   data-collapsed-label, so the markup names the pane once and both strips share
   one rule.

   Collapsed only. Expanded, the pane carries its own heading and the strip is a
   6px drag handle with no room for anything.

   pointer-events:none so the label never eats the click that reopens the pane —
   the whole strip is the control, and the text sits on top of it. */
.editor-body.outline-collapsed #outline-splitter::after,
.editor-body.chat-collapsed #pane-splitter::after {
  content: attr(data-collapsed-label);
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  writing-mode: vertical-rl;
  font-family: var(--ui-font);
  font-size: 11px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-dim, #888);
  white-space: nowrap;
  pointer-events: none;
}
/* The chevrons give up the centre to the name and sit near the top, where they
   still read as the toggle. Overriding `transform` too, or the inherited
   translate(-50%,-50%) would pull them half their height above `top`. */
.editor-body.outline-collapsed #outline-splitter::before,
.editor-body.chat-collapsed #pane-splitter::before {
  top: 14px;
  transform: translateX(-50%);
}
.editor-body.outline-collapsed #outline-splitter:hover::after,
.editor-body.chat-collapsed #pane-splitter:hover::after {
  color: var(--accent, #7a3b2e);
}

/* ---------- Chrome bar ----------
   The thin strip above the editor tabs. Its own class rather than .pane-splitter:
   that one is vertical (col-resize, side borders), and this is a horizontal TOGGLE,
   so it takes a pointer cursor and top/bottom borders. Collapsed it grows a few
   pixels, the same way the pane splitters widen, so the one control that survives
   the fold stays easy to find and hit. */
.chrome-splitter {
  flex-shrink: 0; position: relative; height: 9px; cursor: pointer;
  background: var(--surface2);
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
  transition: background 0.12s, height 0.12s;
}
/* ONE chevron pointing the way the chrome will move, not the stacked pair the pane
   splitters carry: those are drag handles as well as toggles, so the pane can go
   either way and both arrows mean something. This is a pure toggle with a single
   direction. It also renders cleanly at this height — ⌃ and ⌄ do not share a
   baseline, so the pair sat visibly crooked in a 9px strip. */
.chrome-splitter::before {
  content: '⌃';
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  font-size: 12px; line-height: 1;
  color: var(--text-dim, #888); pointer-events: none;
}
body.chrome-collapsed .chrome-splitter::before { content: '⌄'; }
.chrome-splitter:hover { background: var(--accent-mid, rgba(122, 59, 46, 0.4)); }
.chrome-splitter:hover::before { color: var(--accent, #7a3b2e); }
.chrome-splitter:focus-visible { outline: 2px solid var(--accent, #7a3b2e); outline-offset: -2px; }
body.chrome-collapsed .chrome-splitter { height: 14px; }
body.chrome-collapsed .chrome-splitter::before { font-size: 14px; }
/* Everything above the strip: the app bar, the whole document header — menus and
   all, which is what separates this from focus mode, where the menus deliberately
   stay — and the formatting toolbar.

   The app bar is a SIBLING of the views, not part of the editor, so hiding it is
   gated on the editor actually being on screen. Without that gate the collapse
   followed the writer back to the document list, where it hid the app bar — brand,
   Settings, account menu — while the strip that undoes it lives inside #editor-view
   and is therefore gone. The state persists, so a reload did not rescue them
   either. Verified in the browser: the list rendered with no header and no way
   back. The .editor-header and .toolbar rules need no gate; they are inside the
   editor view and vanish with it. */
body.chrome-collapsed:has(#editor-view:not(.hidden)) header,
body.chrome-collapsed .editor-header,
body.chrome-collapsed .toolbar { display: none; }

/* ---------- Focus mode (View ▸ Focus mode, ⌘⇧F) ----------
   The page gets the whole window: the app bar and the document-identity row go,
   and both side panes collapse to zero. Paired with real browser fullscreen it
   leaves the menus, the toolbar and the sheet, and nothing else.

   Deliberately a pure CSS state and NOT a second way to hide the panes. The
   outline/chat collapse classes and their persisted widths are never written, so
   leaving focus mode restores exactly the layout the writer had — including a
   pane they had already collapsed themselves.

   Zero-width columns rather than display:none, for the reason the collapse rules
   above give: display:none lets grid auto-flow shift a splitter into column 1.
   All four collapse permutations are overridden, since focus mode has to win
   whatever the writer's own collapse state was. */
body.focus-mode header { display: none; }
/* Everything in the document header except the menu bar: the back arrow, the
   outline toggle, the connection badge, the title and Share. A writer in focus
   mode still formats, so .editor-actions stays. */
body.focus-mode .editor-header > :not(.editor-actions) { display: none; }
body.focus-mode .editor-body,
body.focus-mode .editor-body.outline-collapsed,
body.focus-mode .editor-body.chat-collapsed,
body.focus-mode .editor-body.outline-collapsed.chat-collapsed {
  grid-template-columns: 0 0 1fr 0 0;
}
/* The splitters have no width left to be clicked in, so silence their chevrons
   rather than leave two hairlines hinting at a control that cannot be hit. */
body.focus-mode #outline-splitter,
body.focus-mode #pane-splitter { pointer-events: none; }
body.focus-mode #outline-splitter::before,
body.focus-mode #pane-splitter::before { content: none; }

.focus-exit { display: none; }
body.focus-mode .focus-exit {
  display: inline-flex; align-items: center; gap: 6px;
  position: fixed; top: 8px; right: 12px; z-index: 70;
  padding: 5px 10px; font-size: 12px; font-family: var(--ui-font);
  color: var(--text-dim); background: var(--surface);
  border: 1px solid var(--border); border-radius: 999px; cursor: pointer;
  opacity: 0.35; transition: opacity 120ms ease;
}
body.focus-mode .focus-exit:hover,
body.focus-mode .focus-exit:focus-visible { opacity: 1; color: var(--accent); }

/* Outline pane */
.outline-pane { background: #fdfcf8; }
.outline-header {
  padding: 6px 14px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--pane-label); background: var(--surface2); border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.outline-list {
  list-style: none; margin: 0; padding: 6px 0; overflow-y: auto; flex: 1;
  font-size: 13px; line-height: 1.4;
}
.outline-list li {
  padding: 3px 14px; cursor: pointer; user-select: none;
  color: var(--text, #222); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.outline-list li:hover { background: rgba(122, 59, 46, 0.08); }
.outline-list li.outline-empty {
  cursor: default; color: var(--text-dim, #888); font-style: italic; white-space: normal;
  padding: 8px 14px; line-height: 1.45;
}
.outline-list li.outline-empty:hover { background: transparent; }
.outline-list li.outline-level-1 { font-weight: 600; }
.outline-list li.outline-level-2 { padding-left: 28px; }
.outline-list li.outline-level-3 { padding-left: 42px; color: var(--text-dim, #555); }
/* Synthesized AI-suggestion entries read as AI notes (purple ✦), matching the
   AI Notes tab / in-text [N] pill. */
.outline-list li.outline-suggestion { color: #7b5cd6; }
.outline-list li.outline-suggestion:hover { background: rgba(123, 92, 214, 0.10); }
/* "Marked words" group — writer-pinned Outline bookmarks, above the headings. */
.outline-list li.outline-group-header {
  cursor: default; user-select: none;
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-dim, #999); padding: 8px 14px 2px; white-space: nowrap;
}
.outline-list li.outline-group-header:hover { background: transparent; }
.outline-list li.outline-bookmark { display: flex; align-items: center; gap: 6px; }
.outline-bookmark-label {
  flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Small blue dot echoes the in-text bookmark cue; follows the row colour so a
   lost bookmark greys out. */
.outline-bookmark-label::before {
  content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 7px;
  border-radius: 50%; background: rgba(50, 110, 200, 0.75); vertical-align: middle; flex: none;
}
.outline-bookmark-actions { flex: 0 0 auto; display: none; gap: 1px; }
.outline-list li.outline-bookmark:hover .outline-bookmark-actions { display: inline-flex; }
.outline-bookmark-btn {
  border: none; background: transparent; cursor: pointer; padding: 0 4px;
  font-size: 12px; line-height: 1; color: var(--text-dim, #888); border-radius: 3px;
}
.outline-bookmark-btn:hover { background: rgba(0, 0, 0, 0.10); color: var(--text, #222); }
.outline-list li.outline-bookmark-lost { cursor: default; color: var(--text-dim, #aaa); }
.outline-list li.outline-bookmark-lost .outline-bookmark-label { text-decoration: line-through; }
.outline-list li.outline-bookmark-lost .outline-bookmark-label::before { background: currentColor; }
.outline-list li.outline-bookmark-lost:hover { background: transparent; }
.pane { display: flex; flex-direction: column; border-right: 1px solid var(--border); overflow: hidden; background: var(--surface); }
.pane:last-child { border-right: none; }
.pane.chat-pane { border-right: none; min-width: 0; }
.pane-splitter {
  cursor: col-resize; background: transparent;
  border-left: 1px solid var(--border); border-right: 1px solid var(--border);
  transition: background 0.12s;
}
.pane-splitter:hover, .pane-splitter.dragging {
  background: var(--accent-mid, rgba(122, 59, 46, 0.4));
  border-left-color: var(--accent, #7a3b2e);
  border-right-color: var(--accent, #7a3b2e);
}
body.col-resizing { cursor: col-resize; user-select: none; }
body.col-resizing iframe { pointer-events: none; }

/* Claude controls (model selector + extended thinking) inside the chat pane */
.claude-controls {
  display: flex; gap: 10px; align-items: center;
  padding: 6px 10px; flex-shrink: 0;
  background: var(--surface2); border-bottom: 1px solid var(--border);
}
.claude-controls .model-select { font-size: 12px; padding: 3px 6px; }
.claude-controls .extended-thinking-label { font-size: 11px; color: var(--text-dim, #888); }
.pane-label {
  padding: 6px 14px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--pane-label); background: var(--surface2); border-bottom: 1px solid var(--border);
  flex-shrink: 0; display: flex; align-items: center; gap: 10px;
}
.pane-hint { font-size: 11px; color: var(--border-strong); text-transform: none; letter-spacing: 0; }

/* Editor pane */
.editor-pane { background: #fdfcf8; position: relative; }

#editor { flex: 1; overflow: auto; font-family: 'Menlo', 'Courier New', monospace; font-size: 14px; }
#editor .cm-editor { height: 100%; }
#editor .cm-scroller { font-family: inherit; overflow: auto; }
#editor .cm-content { padding: 16px 14px; }

/* PDF pane */
.pdf-placeholder {
  flex: 1; display: flex; align-items: center; justify-content: center;
  background: #e8e6e1;
}
.pdf-placeholder-inner { text-align: center; color: var(--text-dim); }
.pdf-icon { font-size: 52px; margin-bottom: 14px; opacity: 0.45; }
.pdf-placeholder-inner p { font-size: 14px; line-height: 1.7; }
#pdf-frame { flex: 1; width: 100%; border: none; background: #888; }
.compile-log {
  flex: 1; padding: 14px; font-family: 'Menlo', 'Courier New', monospace;
  font-size: 12px; color: #a33; background: #fcf6f6; overflow: auto;
  white-space: pre-wrap; line-height: 1.5;
}
.compile-log.hidden { display: none; }

/* Chat pane */
.chat-pane { background: #fdfcf8; }
.chat-messages { flex: 1; overflow-y: auto; padding: 14px; }
.msg { margin-bottom: 18px; font-size: 14px; line-height: 1.6; }
.msg .role {
  display: flex; align-items: center;
  font-family: 'Courier New', monospace; font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--text-dim); margin-bottom: 5px;
}
.msg.user .role { color: var(--accent); }
/* Share-to-team-chat, on the role line. Hidden until the message is hovered
   (or the button focused), so the transcript stays quiet to read. */
.msg-share {
  margin-left: auto; padding: 1px 6px;
  font-family: var(--ui-font); font-size: 10px; letter-spacing: 0;
  text-transform: none; color: var(--text-dim);
  border: 1px solid var(--border); border-radius: 3px; background: none;
  opacity: 0; transition: opacity 0.12s, color 0.12s, border-color 0.12s;
}
.msg:hover .msg-share, .msg-share:focus { opacity: 1; }
.msg-share:hover { color: var(--accent); border-color: var(--accent-mid); background: var(--accent-light); }

/* CodeMirror Find & Replace panel, themed to the app palette. */
.cm-panels { background: var(--surface2); color: var(--text); }
.cm-panels.cm-panels-top { border-bottom: 1px solid var(--border); }
.cm-panel.cm-search { padding: 8px 10px; font-family: var(--ui-font); }
.cm-panel.cm-search label { font-size: 12px; color: var(--text-dim); }
.cm-panel.cm-search .cm-textfield {
  font-size: 13px; padding: 3px 7px; margin: 2px 4px 2px 0;
  border: 1px solid var(--border); border-radius: 5px;
  background: var(--surface); color: var(--text);
}
.cm-panel.cm-search .cm-button {
  font-size: 12px; padding: 3px 9px; border-radius: 5px;
  border: 1px solid var(--border-strong); background: var(--surface);
  background-image: none; color: var(--text); text-transform: none;
}
.cm-panel.cm-search .cm-button:hover { background: var(--accent-light); border-color: var(--accent-mid); }
.cm-panel.cm-search [name=close] { color: var(--text-dim); font-size: 18px; }
/* Your own messages sit in a faint box, a touch darker than the chat pane
   (--chat-pane bg #fdfcf8), so they read as distinct from the AI's replies. */
.msg.user {
  background: #f6f2ec;
  border: 1px solid #efe8dc;
  border-radius: 8px;
  padding: 8px 12px;
}
.msg .content { white-space: pre-wrap; word-wrap: break-word; }
.msg code { font-family: 'Menlo', monospace; font-size: 12px; background: var(--code-bg); padding: 1px 5px; border-radius: 3px; }
.msg pre { background: var(--code-bg); padding: 10px 12px; border-radius: 4px; overflow-x: auto; font-size: 12px; line-height: 1.45; margin: 8px 0; }
.msg pre code { background: none; padding: 0; }

.patch-block { border: 1px solid var(--patch-border); background: var(--patch-bg); border-radius: 4px; margin: 8px 0; overflow: hidden; }
.patch-block pre { background: transparent; margin: 0; border-radius: 0; }
.patch-actions { padding: 7px 10px; border-top: 1px solid var(--patch-border); background: #e4ede4; display: flex; gap: 6px; flex-wrap: wrap; }
.patch-actions button { font-size: 12px; padding: 3px 10px; }

/* AI suggested-edit breadcrumb: the change itself is staged in the document
   as an Accept/Reject tracked change; this chip just records it in the chat. */
.ai-edit-chip { border: 1px solid var(--patch-border); border-left: 3px solid #7b5cd6; background: var(--patch-bg); border-radius: 4px; margin: 8px 0; padding: 8px 10px; }
.ai-edit-chip-head { font-size: 11px; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-dim); }
.ai-edit-chip-diff { font-family: 'Menlo', monospace; font-size: 12px; line-height: 1.5; margin-top: 5px; word-break: break-word; }
.ai-edit-del { color: #b3261e; text-decoration: line-through; }
.ai-edit-ins { color: #1a7f37; }
.ai-edit-chip.is-clickable { cursor: pointer; transition: background 0.12s, border-color 0.12s; }
.ai-edit-chip.is-clickable:hover { background: var(--accent-light); border-left-color: var(--accent); }
.ai-edit-chip.ai-edit-chip-missing { border-left-color: var(--border-strong); opacity: 0.7; cursor: default; }
.chat-note { font-size: 12px; color: var(--text-dim); font-style: italic; margin: 4px 0 10px; }

/* "Include another AI's chat" context picker (in each chat input area) */
.ai-context-ctl { position: relative; display: flex; align-items: center; gap: 8px; margin: 0 0 6px; flex-wrap: wrap; }
.ai-context-btn {
  font-size: 11px; padding: 2px 8px; border-radius: 5px;
  border: 1px solid var(--border, #e0ddd6); background: transparent; color: var(--text-dim, #888); cursor: pointer;
}
.ai-context-btn:hover { border-color: var(--accent-mid, #c47a68); color: var(--accent, #7a3b2e); }
.ai-context-active { font-size: 11px; color: var(--accent, #7a3b2e); font-style: italic; }
.ai-context-menu {
  position: absolute; z-index: 20; bottom: 100%; margin-bottom: 4px; min-width: 150px;
  background: var(--surface, #fff); border: 1px solid var(--border-strong, #ddd);
  border-radius: 6px; box-shadow: 0 4px 14px rgba(0,0,0,0.14); padding: 4px;
}
.ai-context-item {
  display: flex; align-items: center; gap: 7px; padding: 6px 8px; font-size: 13px;
  cursor: pointer; border-radius: 4px; color: var(--text);
}
.ai-context-item:hover { background: var(--accent-light, #f2e4e0); }
.ai-context-item input { accent-color: var(--accent, #7a3b2e); margin: 0; }

.thinking-block { background: #f0eee8; border-left: 3px solid var(--border-strong); padding: 8px 12px; margin-bottom: 8px; border-radius: 0 4px 4px 0; }
.thinking-block summary { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); cursor: pointer; font-family: 'Courier New', monospace; user-select: none; }
.thinking-block pre { background: transparent; font-size: 12px; color: var(--text-dim); white-space: pre-wrap; margin: 6px 0 0; padding: 0; }

/* ChatGPT pane assistant content uses real DOM structure (paragraphs,
   lists, headings) instead of pre-wrap whitespace, so override the default
   .msg .content { white-space: pre-wrap; } and style the markdown blocks. */
#gpt-chat-messages .msg.assistant .content,
#more-chat-messages .msg.assistant .content { white-space: normal; }
#gpt-chat-messages .msg.assistant .content p,
#more-chat-messages .msg.assistant .content p { margin: 0 0 8px 0; }
#gpt-chat-messages .msg.assistant .content p:last-child,
#more-chat-messages .msg.assistant .content p:last-child { margin-bottom: 0; }
#gpt-chat-messages .msg.assistant .content h3,
#gpt-chat-messages .msg.assistant .content h4,
#gpt-chat-messages .msg.assistant .content h5,
#more-chat-messages .msg.assistant .content h3,
#more-chat-messages .msg.assistant .content h4,
#more-chat-messages .msg.assistant .content h5 {
  margin: 12px 0 6px;
  font-size: 1.05em;
  font-weight: 600;
}
#gpt-chat-messages .msg.assistant .content h3:first-child,
#gpt-chat-messages .msg.assistant .content h4:first-child,
#gpt-chat-messages .msg.assistant .content h5:first-child,
#more-chat-messages .msg.assistant .content h3:first-child,
#more-chat-messages .msg.assistant .content h4:first-child,
#more-chat-messages .msg.assistant .content h5:first-child { margin-top: 0; }
#gpt-chat-messages .msg.assistant .content ul,
#gpt-chat-messages .msg.assistant .content ol,
#more-chat-messages .msg.assistant .content ul,
#more-chat-messages .msg.assistant .content ol {
  margin: 6px 0 8px; padding-left: 22px;
}
#gpt-chat-messages .msg.assistant .content li,
#more-chat-messages .msg.assistant .content li { margin: 2px 0; }
#gpt-chat-messages .msg.assistant .content strong,
#more-chat-messages .msg.assistant .content strong { font-weight: 600; }
#gpt-chat-messages .msg.assistant .content em,
#more-chat-messages .msg.assistant .content em { font-style: italic; }

/* Context panel */
.context-panel { border-top: 1px solid var(--border); background: var(--surface2); flex-shrink: 0; }
.context-panel-fullpane { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.context-panel-fullpane .context-list { max-height: none; flex: 1 1 auto; padding: 8px 14px; }
.context-panel-fullpane .context-list li { font-size: 13px; padding: 6px 0; }
.context-header { display: flex; align-items: center; padding: 5px 10px; gap: 8px; }
.context-title { font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-dim); font-family: 'Courier New', monospace; flex: 1; }
.context-add-btn { font-size: 12px; padding: 3px 8px; color: var(--accent); border-color: var(--accent-mid); }
.context-list { list-style: none; padding: 0 10px 6px; max-height: 90px; overflow-y: auto; }
.context-list li { display: flex; align-items: center; gap: 6px; padding: 3px 0; font-size: 12px; }
.context-list .ctx-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.context-list .ctx-size { color: var(--text-dim); font-family: 'Courier New', monospace; font-size: 11px; }
.context-list .ctx-toggle { accent-color: var(--accent); }
.context-list .ctx-remove { padding: 1px 5px; font-size: 11px; color: var(--text-dim); border-color: var(--border); }
.context-list .ctx-loading { color: var(--text-dim); font-style: italic; }
.context-list .ctx-share { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--text-dim); white-space: nowrap; cursor: pointer; }
.context-list .ctx-share .ctx-share-toggle { accent-color: var(--accent); margin: 0; }
.context-list .ctx-owner { color: var(--text-dim); font-style: italic; font-size: 11px; }
.context-list li.ctx-shared-in .ctx-name { font-style: italic; }

/* Coach tab — the house AI. Deliberately plain: the pane has one control and one
   budget line, because everything else about this AI is fixed server-side. */
.coach-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.coach-budget { font-size: 11px; color: var(--text-dim); white-space: nowrap; }
.coach-budget-low { color: var(--accent); }
.coach-intro { padding: 14px 14px 4px; color: var(--text-dim); font-size: 12.5px; line-height: 1.45; }
.coach-intro h4 { margin: 0 0 6px; font-size: 13px; color: var(--text); }
.coach-intro p { margin: 0 0 8px; }
/* The provider disclosure, at the point of use rather than only in the policy. */
.coach-intro-fine { font-size: 11px; line-height: 1.4; opacity: 0.85; }
/* Marks the turns that cost several times more, so the budget line has a visible
   cause rather than dropping unexplained. */
.coach-scope-tag { font-size: 10px; color: var(--text-dim); font-style: italic; margin-top: 2px; }

/* Skills tab */
.context-hint { margin: 0; padding: 0 12px 6px; font-size: 11px; color: var(--text-dim); line-height: 1.35; }
/* Per-app install steps, closed by default: the panel should open on the skills,
   not on a manual, and a writer needs these once. */
.skill-portability { margin: 0 12px 8px; font-size: 11px; color: var(--text-dim); line-height: 1.4; }
.skill-portability > summary { cursor: pointer; color: var(--accent); }
.skill-portability p { margin: 6px 0 0; }
.skill-portability code { font-size: 10px; background: var(--accent-light, rgba(0,0,0,.05)); padding: 0 3px; border-radius: 3px; }
.skills-section-label { padding: 6px 12px 2px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); }
.skill-toggle { accent-color: var(--accent); }
.skill-view { padding: 1px 6px; font-size: 11px; color: var(--accent); border-color: var(--accent-mid); white-space: nowrap; }
/* Sits beside edit / view: same chip, narrowed to its arrow so the row's scope
   and share controls keep their width. */
.skill-download { padding: 1px 5px; font-size: 11px; line-height: 1.4; color: var(--accent); border-color: var(--accent-mid); white-space: nowrap; }
.context-list li.skill-builtin, .context-list li.ctx-mine, .context-list li.ctx-shared-in { flex-wrap: wrap; }
.skill-desc { flex-basis: 100%; color: var(--text-dim); font-size: 11px; line-height: 1.35; padding-left: 22px; }
/* A skill's text is no longer shown inline in the row: both "edit" (built-ins)
   and "view" (user skills) open the .skill-editor-card hovering pane instead, so
   the .skill-body / .skill-edit box styles that lived here are gone. */
.skill-edit-status { font-size: 11px; color: var(--text-dim); }
.skill-edited { color: var(--text-dim); font-size: 11px; font-style: italic; margin-left: 4px; }
/* Capped so the controls line holds view, download, scope, share and remove
   together in a 400px pane; 118px is the width at which the widest option
   ("All my documents") still reads under the caret. The share label is short for
   the same reason; both controls keep their explanatory title. */
.skill-scope { font-size: 11px; padding: 1px 4px; max-width: 118px; }
/* A user skill carries five controls, which at this pane width left the name a
   dozen pixels — a skill you cannot read the name of. The name therefore takes
   the first line beside its checkbox and the controls wrap to the second, which
   is a deliberate two-line row rather than a squeezed one. Built-in rows are
   unaffected: they carry only edit and download, and already run a description
   line underneath. */
#skills-user-list li .ctx-name { flex: 1 1 calc(100% - 26px); min-width: 0; }
.skill-scope-label { font-size: 11px; color: var(--text-dim); font-style: italic; }
.skill-empty { color: var(--text-dim); font-style: italic; }
/* Skill authoring form */
.skill-form { display: flex; flex-direction: column; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--border); }
.skill-form input[type=text], .skill-form textarea { width: 100%; box-sizing: border-box; font-size: 13px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); }
.skill-form textarea { min-height: 90px; resize: vertical; font-family: inherit; }
.skill-form-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; }
.skill-form-row label { display: inline-flex; align-items: center; gap: 6px; color: var(--text-dim); }
.skill-form-actions { display: inline-flex; gap: 6px; }

/* Chat input */
.chat-input-area { border-top: 1px solid var(--border); padding: 10px; background: var(--surface); }
.chat-option { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-dim); margin-bottom: 7px; cursor: pointer; }
.chat-option input { accent-color: var(--accent); }
/* "Show to coauthors": the per-thread sharing switch, sitting with the other
   send options rather than hidden as a badge in the thread dropdown. The icon
   carries the state as well as the checkbox does — a lock when the conversation
   is yours alone, faces once coauthors can read it. */
.collab-option .collab-icon { font-size: 12px; line-height: 1; }
.collab-option.is-shared { color: var(--accent, #7a3b2e); }
/* Someone else's shared conversation: readable, joinable, but not yours to
   close. The control states whose it is instead of offering a dead checkbox. */
.collab-option.is-theirs { cursor: default; font-style: italic; }
.collab-option.is-theirs input { cursor: default; }
#chat-input,
#coach-input {
  width: 100%; min-height: 68px; max-height: 180px; padding: 8px 10px;
  font-family: inherit; font-size: 14px; border: 1px solid var(--border-strong);
  border-radius: 4px; resize: vertical; background: var(--surface); margin-bottom: 8px; line-height: 1.5;
}
#chat-input:focus,
#coach-input:focus { outline: 2px solid var(--accent); border-color: var(--accent); }
#send-btn,
#coach-send { width: 100%; background: var(--accent); color: white; border-color: var(--accent); }
#send-btn:hover,
#coach-send:hover { background: #923f30; border-color: #923f30; color: white; }
#send-btn:disabled,
#coach-send:disabled { opacity: 0.45; }
/* Stop button: shown beside Send while a request is in flight (all panes). */
.stop-btn { width: 100%; margin-top: 6px; background: #b33; color: white; border-color: #b33; }
.stop-btn:hover { background: #962a2a; border-color: #962a2a; color: white; }
/* ChatGPT input panel inherits the same pinned-at-bottom layout as Claude:
   the surrounding .chat-input-area has `border-top` + `padding`, and the
   textarea / send button match the Claude sizing so the user sees one
   identical writing panel regardless of which provider is active. */
#gpt-chat-input {
  width: 100%; min-height: 68px; max-height: 180px; padding: 8px 10px;
  font-family: inherit; font-size: 14px; border: 1px solid var(--border-strong);
  border-radius: 4px; resize: vertical; background: var(--surface); margin-bottom: 8px; line-height: 1.5;
  box-sizing: border-box;
}
#gpt-chat-input:focus { outline: 2px solid var(--accent); border-color: var(--accent); }
#gpt-send-btn { width: 100%; background: var(--accent); color: white; border-color: var(--accent); }
#gpt-send-btn:hover { background: #923f30; border-color: #923f30; color: white; }
#gpt-send-btn:disabled { opacity: 0.45; }
.thinking { font-style: italic; color: var(--text-dim); padding: 6px 0; font-size: 13px; }

/* Document menu */
.doc-menu-wrap { position: relative; }
.doc-menu-btn {
  background: transparent; color: var(--text);
  border: 1px solid transparent; padding: 6px 13px;
  font-size: 14px; border-radius: 4px; cursor: pointer; white-space: nowrap;
}
.doc-menu-btn:hover { background: var(--accent-light); color: var(--accent); }
/* Compile is a primary action — accent-filled so it is easy to find from
   any tab (Clean view in particular, which has no other compile control). */
#header-compile-btn { background: var(--accent, #7a3b2e); border-color: var(--accent, #7a3b2e); color: #fff; }
#header-compile-btn:hover { background: var(--accent-dark, #5a2a1e); border-color: var(--accent-dark, #5a2a1e); color: #fff; }
#header-compile-btn:disabled { opacity: 0.55; cursor: default; }
/* Check & fix appears only after a failed compile — warning-tinted. */
/* Online / offline indicator (P1-7) */
.connection-badge {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 10px;
  background: rgba(45, 122, 45, 0.10);
  color: #1f5a1f;
  border: 1px solid rgba(45, 122, 45, 0.35);
  font-family: 'Courier New', monospace;
  white-space: nowrap;
}
.connection-badge.is-offline {
  background: rgba(180, 50, 50, 0.10);
  color: #a02020;
  border-color: rgba(180, 50, 50, 0.45);
}
/* Local folder sync pill (P1-8). Shows folder name + last-synced age when
   the writer has enabled "Sync .docx to a folder" for this doc. Clicking
   opens the change-folder / stop-syncing popover via the same menu item. */
.local-sync-pill {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 10px;
  background: rgba(122, 59, 46, 0.08);
  color: var(--accent, #7a3b2e);
  border: 1px solid rgba(122, 59, 46, 0.30);
  font-family: 'Courier New', monospace;
  white-space: nowrap;
  cursor: pointer;
}
.local-sync-pill:hover {
  background: rgba(122, 59, 46, 0.14);
}
.local-sync-pill.hidden { display: none; }
.local-sync-pill.is-syncing { opacity: 0.6; }
.local-sync-pill.is-error {
  background: rgba(180, 50, 50, 0.10);
  color: #a02020;
  border-color: rgba(180, 50, 50, 0.45);
}
/* "offline ✓" pill next to a doc in the landing list. Quieter than the
   header badge — informational, not a status. */
.doc-offline-pill {
  font-size: 10px;
  padding: 2px 6px;
  margin-left: 6px;
  border-radius: 8px;
  background: rgba(122, 59, 46, 0.08);
  color: var(--accent, #7a3b2e);
  border: 1px solid rgba(122, 59, 46, 0.25);
  font-family: 'Courier New', monospace;
  vertical-align: middle;
}
#header-fix-btn { background: #fbeadb; border-color: rgba(180, 70, 30, 0.45); color: rgba(150, 45, 20, 0.95); }
#header-fix-btn:hover { background: #f6dcc6; border-color: rgba(180, 70, 30, 0.65); color: rgba(150, 45, 20, 1); }
#header-fix-btn.is-hidden { display: none; }
/* Mirror Editor: the Clean-view-era LaTeX repair controls operate on the
   CodeMirror source, which the model owns and normalizes, so they no-op here.
   Hide them when the model editor is active (body.mirror-active, set in openDoc)
   rather than leave dead controls that silently do nothing. !important beats
   #header-fix-btn's own is-hidden toggle so a failed compile can't re-show it. */
body.mirror-active #cleanup-menu-btn,
body.mirror-active #header-fix-btn,
body.mirror-active #ref-convert-linked-btn { display: none !important; }
/* Grammar pair: main button + caret expand menu. The two render as a
   contiguous button group; the caret is narrower so the pair reads as one
   control. */
#header-grammar-btn { background: transparent; border-color: transparent; color: var(--text); border-top-right-radius: 0; border-bottom-right-radius: 0; }
#header-grammar-btn:hover { background: var(--accent-light); color: var(--accent); }
#header-grammar-btn:disabled { opacity: 0.55; cursor: default; }
.doc-menu-caret { padding: 6px 7px; border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-width: 0; }
/* .doc-menu sets `overflow: hidden` with no max-height, so a menu taller than the
   viewport is silently CLIPPED rather than scrolled — the bottom items simply cease to
   exist, with nothing on screen to say so. The Grammar menu is the one that grew past
   that line (adding the built-in checker section made it 113px taller, which put
   "Clear all findings" below the fold on a laptop-height window), so it scrolls and is
   capped to the viewport by the open handler. */
.doc-menu.grammar-menu { min-width: 240px; padding: 4px 0; overflow-y: auto; overscroll-behavior: contain; }
.grammar-menu .grammar-menu-section { padding: 4px 0; }
.grammar-menu .grammar-menu-label { padding: 4px 16px 6px; font-size: 12px; color: var(--text-dim, #888); font-family: var(--ui-font); }
.grammar-menu .grammar-menu-sublabel { padding: 0 16px 6px; font-size: 11px; line-height: 1.35; color: var(--text-dim, #888); opacity: 0.8; font-family: var(--ui-font); max-width: 280px; }
.grammar-menu .menu-item.is-disabled { opacity: 0.4; cursor: not-allowed; }
.grammar-menu .menu-item.is-disabled:hover { background: none; }
#grammar-status.warn { color: #b8730a; font-weight: 600; }
#grammar-status.ok { color: #2d7a2d; font-weight: 600; }
#grammar-status.error { color: #b33; font-weight: 600; }
#fix-modal-apply.is-hidden { display: none; }
.fix-issue-list { margin: 0; padding-left: 20px; }
.fix-issue-list li { font-size: 13px; color: var(--text); margin: 5px 0; line-height: 1.4; }
.doc-menu {
  position: absolute; top: calc(100% + 4px); right: 0;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 5px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);
  min-width: 210px; z-index: 100;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
}
.doc-menu.hidden { display: none; }
.menu-item {
  display: block; width: 100%; text-align: left;
  padding: 10px 16px; font-size: 14px; font-family: var(--ui-font);
  background: none; border: none; border-radius: 0; cursor: pointer;
  color: var(--text);
}
.menu-item:hover { background: var(--accent-light); color: var(--accent); border-color: transparent; }
.menu-divider { height: 1px; background: var(--border); margin: 2px 0; }

/* Two-stage (flyout) submenus — Google Docs' "Format ▸ Capitalization ▸ UPPERCASE".
   A `.menu-sub` wraps a `.menu-item.has-sub` trigger and a nested `.doc-menu.menu-flyout`
   that opens on hover/focus beside the parent item. The parent dropdown must not clip it,
   and because a menu can SCROLL, the flyout is `position: fixed` so no ancestor's overflow
   can clip it — placed against its trigger by placeFlyout (app.js), which picks the side with
   room and keeps the whole panel on screen. That is what lets a dropdown scroll AND show its
   submenus: the previous answer was to give the menu `overflow: visible`, which revealed the
   flyouts at the cost of the scroll, so a capped menu simply spilled off the bottom instead. */
.menu-sub { position: relative; }
.menu-item.has-sub { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.menu-item.has-sub::after { content: '\25B8'; font-size: 12px; opacity: 0.55; }
/* A flyout is deliberately NOT a `.doc-menu`: every close-all handler in app.js adds `.hidden`
   to `.doc-menu, .tb-menu`, which force-closed the flyouts the moment any menu opened. It carries
   its own panel styling instead, and its open/closed state is CSS-only (hover/focus). */
.menu-flyout {
  position: fixed; top: 0; left: 0; min-width: 190px; display: none;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 5px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);
  z-index: 101; padding: 2px 0;
  /* A submenu can outgrow the viewport too (Page number is seven items plus a hint);
     placeFlyout sets the cap, this makes it scroll rather than run off the bottom. */
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
}
.menu-sub:hover > .menu-flyout,
.menu-sub:focus-within > .menu-flyout { display: block; }

/* Format menu. The height cap is NOT declared here: a fixed `70vh` is wrong whenever the
   button does not sit at the top of the viewport, so clampMenuToViewport measures the real
   space below the button instead. The collapsible `<details>` sections this menu carried are
   now flyouts like its other submenus (2026-08-25) — expanding one in place grew the menu
   past the bottom of the screen, which is the failure the .doc-menu note above describes. */
.doc-menu.format-menu { min-width: 240px; }
.format-indent-row { display: flex; align-items: center; gap: 8px; padding: 6px 14px; font-size: 13px; color: var(--text-dim); }
.format-indent-row input { width: 64px; padding: 3px 6px; }
.format-indent-hint { padding: 0 14px 8px; font-size: 11px; color: var(--text-dim); max-width: 260px; line-height: 1.35; }
.format-para-label { display: inline-block; width: 44px; }
.format-para-line-row { display: flex; align-items: center; gap: 4px; padding: 2px 14px 4px; }
.format-para-line-row .menu-item { padding: 3px 8px; border-radius: 4px; }

/* ---------- Thread controls ---------- */
.thread-controls {
  display: flex; align-items: center; gap: 6px;
  margin-left: auto;
}
.thread-selector-wrap { position: relative; }
.thread-selector-btn {
  font-size: 12px; padding: 3px 9px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 4px; cursor: pointer; color: var(--text);
  max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--ui-font);
}
.thread-selector-btn:hover { background: var(--accent-light); border-color: var(--accent); color: var(--accent); }
.thread-dropdown {
  position: absolute; top: calc(100% + 4px); right: 0;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 5px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);
  min-width: 240px; max-height: 280px; overflow-y: auto;
  z-index: 200;
}
.thread-dropdown.hidden { display: none; }
.thread-item {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 12px; cursor: pointer; border-bottom: 1px solid var(--border);
  transition: background 0.1s;
}
.thread-item:last-child { border-bottom: none; }
.thread-item:hover { background: var(--accent-light); }
.thread-item.active { background: var(--accent-light); }
.thread-item-title {
  flex: 1; font-size: 13px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.thread-item-date { font-size: 11px; color: var(--text-dim); font-family: 'Courier New', monospace; white-space: nowrap; }
.thread-item-del, .thread-item-share, .thread-item-collab {
  padding: 1px 6px; font-size: 11px; color: var(--text-dim);
  border: 1px solid var(--border); border-radius: 3px; background: none;
  flex-shrink: 0;
}
.thread-item-del:hover { color: #a33; border-color: #a33; background: #fcf6f6; }
.thread-item-share:hover { color: var(--accent); border-color: var(--accent-mid); background: var(--surface); }
/* Collaborative conversation: 👥 when open to coauthors, 🔒 when private. Filled
   in when on, so the state reads at a glance down the list. */
.thread-item-collab.on { border-color: var(--accent-mid); background: var(--surface); }
.thread-item-collab:disabled { opacity: 0.75; cursor: default; }
.thread-item-collab:not(:disabled):hover { border-color: var(--accent-mid); background: var(--surface); }
.new-thread-btn {
  font-size: 12px; padding: 3px 9px;
  color: var(--accent); border-color: var(--accent-mid);
  border-radius: 4px; white-space: nowrap;
}
.thread-empty {
  padding: 14px 12px; font-size: 13px;
  color: var(--text-dim); text-align: center; font-style: italic;
}

/* ---------- References panel ---------- */
.ref-panel {
  border-top: 1px solid var(--border);
  background: var(--surface2);
  flex-shrink: 0;
  max-height: 260px;
  display: flex;
  flex-direction: column;
}
.ref-header {
  display: flex; align-items: center; padding: 5px 10px; gap: 6px;
  flex-shrink: 0;
}
.ref-title {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--text-dim); font-family: 'Courier New', monospace; flex: 1;
}
.ref-header-actions { display: flex; gap: 5px; }
.ref-action-btn {
  font-size: 11px; padding: 2px 7px;
  color: var(--accent); border-color: var(--accent-mid); border-radius: 3px;
}
.ref-tabs {
  display: flex; gap: 4px; padding: 0 10px 6px; flex-shrink: 0;
}
.ref-tab {
  font-size: 11px; padding: 3px 10px; border-radius: 3px;
  background: transparent; border: 1px solid var(--border-strong);
  color: var(--text-dim); cursor: pointer;
}
.ref-tab.active {
  background: var(--accent-mid, rgba(122, 59, 46, 0.12));
  border-color: var(--accent, #7a3b2e);
  color: var(--accent, #7a3b2e);
}
.ref-tab:hover:not(.active) { background: rgba(0,0,0,0.04); }

.ref-connect {
  display: flex; gap: 6px; padding: 4px 10px 6px; flex-shrink: 0;
}
.ref-connect button {
  font-size: 11px; padding: 3px 10px; border-radius: 3px;
  border: 1px solid var(--accent-mid); color: var(--accent); cursor: pointer;
  background: transparent;
}
.ref-connect button:hover { background: rgba(122, 59, 46, 0.08); }
.ref-connect.hidden, .ref-connect button.hidden { display: none; }

/* EndNote tab: file-based RIS export/import (EndNote has no live API). */
.ref-endnote-panel { padding: 10px 12px; flex-shrink: 0; }
.ref-endnote-panel.hidden { display: none; }
.ref-endnote-note { font-size: 12px; color: var(--text-muted, #6b5d52); margin: 0 0 10px; line-height: 1.5; }
.ref-endnote-note code { font-size: 11px; background: rgba(0,0,0,0.05); padding: 1px 4px; border-radius: 3px; }
.ref-endnote-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.ref-endnote-hint { font-size: 11px; color: var(--text-muted, #8a7c70); margin-top: 8px; }
.ref-endnote-imported-head { font-size: 12px; color: var(--accent, #7a3b2e); padding: 6px 4px; font-weight: 500; }

.ref-search-row {
  display: flex; gap: 6px; padding: 0 10px 6px; flex-shrink: 0;
}
.ref-search-row input {
  flex: 1; padding: 5px 8px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--border-strong); border-radius: 3px; background: var(--surface);
}
.ref-search-row input:focus { outline: 1px solid var(--accent); border-color: var(--accent); }
.ref-search-row button { font-size: 13px; padding: 4px 10px; }
#ref-refresh-btn { font-size: 15px; line-height: 1; }
#ref-refresh-btn.refreshing { animation: ref-refresh-spin 0.7s linear infinite; }
@keyframes ref-refresh-spin { to { transform: rotate(360deg); } }
.ref-results {
  overflow-y: auto; flex: 1;
  padding: 0 10px 6px;
}
/* Live search refreshes this list while the user is still typing. Dimming the
   current hits reads as "these are being replaced" without the flash of
   emptying the pane; the delay keeps a fast reply from registering as a blink. */
.ref-results.is-searching { opacity: 0.55; transition: opacity 120ms ease 90ms; }
.ref-item {
  border: 1px solid var(--border); border-radius: 4px;
  background: var(--surface); padding: 7px 9px;
  margin-bottom: 5px; font-size: 12px; line-height: 1.45;
}
.ref-item-title {
  font-weight: bold; color: var(--text); margin-bottom: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ref-item-meta { color: var(--text-dim); font-size: 11px; margin-bottom: 5px; }
.ref-item-actions { display: flex; gap: 5px; flex-wrap: wrap; }
.ref-item-actions button { font-size: 11px; padding: 2px 8px; }
.ref-cite-btn { color: var(--accent); border-color: var(--accent-mid); }
.ref-loading { padding: 10px; font-size: 13px; color: var(--text-dim); font-style: italic; text-align: center; }
.ref-empty { padding: 10px; font-size: 13px; color: var(--text-dim); text-align: center; }
.ref-error { padding: 10px; font-size: 12px; color: #a33; }
.ref-uploaded-badge {
  font-size: 11px; color: #2d7a2d; padding: 3px 8px;
  background: #eef4ee; border: 1px solid var(--patch-border);
  border-radius: 3px; margin: 0 10px 6px;
}

/* Citation style row in menu */
.menu-style-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 16px; border-bottom: 1px solid var(--border);
}
.menu-style-label { font-size: 13px; color: var(--text-dim); white-space: nowrap; }
.citation-style-select {
  flex: 1; padding: 4px 6px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--border-strong); border-radius: 3px;
  background: var(--surface); color: var(--text); cursor: pointer;
}

/* Citation style in toolbar */
.tb-label {
  font-size: 11px; color: var(--text-dim); text-transform: uppercase;
  letter-spacing: 0.5px; font-family: 'Courier New', monospace; white-space: nowrap;
}
.tb-select {
  padding: 3px 6px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--border); border-radius: 3px;
  background: var(--surface); color: var(--text); cursor: pointer;
}
.tb-select:focus { outline: 1px solid var(--accent); border-color: var(--accent); }

/* ---------- Modal (image manager) ---------- */
.modal-backdrop {
  position: fixed; top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.42);
  display: flex; align-items: center; justify-content: center;
  z-index: 200;
}
.modal-backdrop.hidden { display: none; }
.modal {
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 6px; box-shadow: 0 10px 40px rgba(0,0,0,0.22);
  min-width: 480px; max-width: 720px; max-height: 80vh;
  display: flex; flex-direction: column;
}
.modal-header {
  padding: 12px 18px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
}
.modal-header h3 { margin: 0; font-size: 15px; font-family: var(--ui-font); }
.modal-close {
  background: none; border: none; font-size: 22px; cursor: pointer;
  color: var(--text-dim); padding: 0 4px; line-height: 1;
}
.modal-close:hover { color: var(--text); }
.modal-body { padding: 14px 18px; overflow-y: auto; flex: 1; }

/* Admin user-roster modal */
.admin-modal { min-width: 640px; max-width: 900px; }
.admin-count { color: var(--text-dim); font-weight: 400; font-size: 13px; }
.admin-note { margin: 0 0 12px; font-size: 12.5px; color: var(--text-dim); }
.admin-status { font-size: 13px; color: var(--text-dim); padding: 8px 0; }
.admin-table-wrap { overflow-x: auto; }
.admin-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.admin-table th, .admin-table td {
  text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.admin-table th {
  font-family: var(--ui-font); font-weight: 600; color: var(--text-dim);
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
}
.admin-table td.num, .admin-table th.num { text-align: right; }
.admin-table tbody tr:hover { background: var(--hover, rgba(127,127,127,.06)); }
.admin-role-badge {
  font-size: 11px; padding: 1px 7px; border-radius: 10px;
  background: rgba(127,127,127,.14); color: var(--text-dim);
}
.admin-role-badge.owner { background: rgba(122,59,46,.16); color: #7a3b2e; }
/* Admin: give Pro for a fixed period (server/pro-grants.js). */
.admin-section-title {
  margin: 0 0 8px; font-family: var(--ui-font); font-size: 11.5px; font-weight: 600;
  color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em;
}
.admin-grants { margin-bottom: 18px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
.admin-grant-form { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.admin-grant-form input {
  flex: 1; min-width: 0; padding: 6px 9px; font-size: 13px;
  border: 1px solid var(--border-strong); border-radius: 4px;
}
.admin-grant-form select {
  padding: 6px 8px; font-size: 13px; background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 4px;
}
.admin-grant-msg { font-size: 13px; margin: 0 0 8px; }
.admin-grant-msg:empty { display: none; }

/* OneDrive file browser modal — bigger panel because users will scroll
   through nested folders, and a dedicated footer with the Add button. */
.onedrive-modal-card { min-width: 560px; max-width: 720px; min-height: 360px; max-height: 76vh; }
.onedrive-body { padding: 12px 16px; }
.onedrive-breadcrumbs { font-size: 13px; margin-bottom: 8px; color: var(--text-dim); }
.onedrive-crumb { background: none; border: none; color: var(--accent, #7a3b2e); cursor: pointer; padding: 0; font: inherit; }
.onedrive-crumb:hover { text-decoration: underline; }
.onedrive-crumb-sep { color: var(--text-dim); }
.onedrive-list { list-style: none; padding: 0; margin: 0; max-height: 48vh; overflow-y: auto; border: 1px solid var(--border); border-radius: 4px; }
.onedrive-item { border-bottom: 1px solid var(--border); }
.onedrive-item:last-child { border-bottom: none; }
.onedrive-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 12px; background: none; border: none; text-align: left; cursor: pointer; font: inherit; }
.onedrive-folder .onedrive-row:hover, .onedrive-file .onedrive-row:hover { background: rgba(0,0,0,0.04); }
.onedrive-icon { width: 18px; text-align: center; }
.onedrive-name { flex: 1; word-break: break-word; }
.onedrive-size { color: var(--text-dim); font-size: 12px; }
.onedrive-check { margin: 0 4px 0 0; }
.onedrive-loading { padding: 16px; color: var(--text-dim); text-align: center; font-size: 13px; }
.onedrive-loading.hidden { display: none; }
.onedrive-empty { padding: 16px; color: var(--text-dim); font-style: italic; text-align: center; }
.modal-footer { padding: 10px 16px; border-top: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.onedrive-status { font-size: 12px; color: var(--text-dim); }
.primary-btn { padding: 6px 14px; background: var(--accent, #7a3b2e); color: white; border: 1px solid var(--accent, #7a3b2e); border-radius: 4px; cursor: pointer; font-size: 13px; }
.primary-btn:hover { background: #923f30; border-color: #923f30; }

/* Insert-link modal. Reuses .modal-backdrop / .modal / .modal-footer / .primary-btn;
   only the field stacking and the compact width are local. */
.link-modal-card { width: 420px; max-width: 92vw; }
.link-modal-card .modal-body { display: flex; flex-direction: column; gap: 12px; padding: 16px; }
.link-field { display: flex; flex-direction: column; gap: 4px; }
.link-field span { font-size: 12px; color: var(--text-dim); }
.link-field input {
  padding: 7px 9px; font-size: 14px; border: 1px solid var(--border);
  border-radius: 4px; background: var(--surface); color: var(--text); font-family: var(--ui-font);
}
.link-field input:focus { outline: none; border-color: var(--accent-mid, #b8837a); }
.link-modal-hint { font-size: 12px; color: var(--text-dim); flex: 1; }
.link-modal-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* Title and authors. Borrows .link-field for the labelled rows; only the textareas
   and the date row are its own. The title box is set in the document's own display
   weight so the writer is editing something that looks like the title. */
.title-meta-card { width: 480px; max-width: 94vw; }
.title-meta-card .modal-body { display: flex; flex-direction: column; gap: 14px; }
.title-meta-input {
  padding: 7px 9px; font-size: 14px; border: 1px solid var(--border);
  border-radius: 4px; background: var(--surface); color: var(--text);
  font-family: var(--ui-font); line-height: 1.4; resize: vertical;
}
.title-meta-input:focus { outline: none; border-color: var(--accent-mid, #b8837a); }
#title-meta-title { font-size: 16px; font-weight: 600; }
.title-meta-sub { font-weight: 400; opacity: 0.75; }
.title-meta-sub::before { content: '— '; }
.title-meta-date-row { display: flex; gap: 8px; align-items: center; }
.title-meta-date-row > * { min-width: 0; }
.title-meta-select {
  padding: 7px 9px; font-size: 13px; border: 1px solid var(--border);
  border-radius: 4px; background: var(--surface); color: var(--text); font-family: var(--ui-font);
}
.title-meta-date-row input { flex: 1; }

/* Fix broken hyphenation: a review list, so it is sized to be read and scrolled rather than
   glanced at. A suggested join is tinted and its change line is what the eye lands on; the
   reason sits under it, because the user is being asked to judge a verdict, not obey it. */
.hyphen-modal-card { width: 560px; max-width: 94vw; }
.hyphen-modal-card .modal-body { padding: 14px 16px; }
.hyphen-intro { margin: 0 0 10px; font-size: 13px; color: var(--text-dim); line-height: 1.5; }
.hyphen-list { display: flex; flex-direction: column; gap: 2px; max-height: 46vh; overflow-y: auto; }
.hyphen-row {
  display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px;
  border-radius: 5px; cursor: pointer; border: 1px solid transparent;
}
.hyphen-row:hover { background: var(--surface-alt, rgba(0, 0, 0, .03)); }
.hyphen-row.is-suggested { background: rgba(184, 131, 122, .09); border-color: rgba(184, 131, 122, .28); }
.hyphen-row input { margin-top: 3px; flex-shrink: 0; }
.hyphen-row-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hyphen-change { font-size: 14px; }
.hyphen-change del { color: var(--text-dim); text-decoration-thickness: 1px; }
.hyphen-why { font-size: 12px; color: var(--text-dim); }
.hyphen-context { font-size: 12px; color: var(--text-dim); opacity: .75; font-style: italic; overflow-wrap: anywhere; }
.hyphen-hint { font-size: 12px; color: var(--text-dim); flex: 1; }

/* Find & Replace bar: a floating panel pinned to the top-right of the viewport, above the editor.
   Not a modal — no backdrop, the document stays visible and editable behind it. */
.find-bar {
  position: fixed; top: 96px; right: 24px; z-index: 60;
  background: var(--surface); border: 1px solid var(--border-strong, var(--border));
  border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,0.18);
  padding: 8px; display: flex; flex-direction: column; gap: 6px; width: 340px; max-width: 92vw;
}
.find-bar.hidden { display: none; }
.find-row { display: flex; align-items: center; gap: 4px; }
.find-input {
  flex: 1; min-width: 0; padding: 6px 8px; font-size: 13px; font-family: var(--ui-font);
  border: 1px solid var(--border); border-radius: 4px; background: var(--surface); color: var(--text);
}
.find-input:focus { outline: none; border-color: var(--accent-mid, #b8837a); }
.find-count { font-size: 12px; color: var(--text-dim); white-space: nowrap; padding: 0 4px; min-width: 62px; text-align: right; }
.find-icon-btn {
  flex-shrink: 0; min-width: 26px; height: 26px; padding: 0 6px; font-size: 12px; line-height: 1;
  background: transparent; border: 1px solid transparent; border-radius: 4px; color: var(--text); cursor: pointer;
}
.find-icon-btn:hover { background: var(--accent-light); border-color: var(--accent-mid); color: var(--accent); }
.find-icon-btn[aria-pressed="true"] { background: var(--accent-light); border-color: var(--accent-mid); color: var(--accent); font-weight: 600; }
.find-close { font-size: 16px; }
.find-text-btn {
  flex-shrink: 0; padding: 5px 10px; font-size: 12px; font-family: var(--ui-font);
  background: var(--surface); border: 1px solid var(--border); border-radius: 4px; color: var(--text); cursor: pointer;
}
.find-text-btn:hover { background: var(--accent-light); border-color: var(--accent-mid); color: var(--accent); }
/* Match highlights drawn on the page-box overlay. The active match is brighter; the others are a
   faint wash. pointer-events:none so clicks fall through to the text underneath. */
.pgx-findrect { position: fixed; background: rgba(250, 204, 21, 0.34); pointer-events: none; z-index: 1; border-radius: 1px; }
.pgx-findrect.is-active { background: rgba(249, 146, 21, 0.55); }

.equation-loading { padding: 20px; color: var(--text-dim); text-align: center; font-size: 13px; }
.equation-host { min-height: 120px; display: flex; align-items: center; justify-content: center; padding: 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface2); }
.equation-host math-field {
  width: 100%; min-height: 80px; font-size: 22px;
  background: #fff; padding: 10px; border-radius: 4px;
  --selection-background-color: var(--accent-light);
  --selection-color: var(--text);
}
.equation-actions { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; gap: 12px; flex-wrap: wrap; }
.equation-mode-label { font-size: 12px; color: var(--text-dim); display: flex; align-items: center; gap: 6px; cursor: pointer; }
.equation-buttons { display: flex; gap: 8px; margin-left: auto; }
/* Structures strip: multi-part templates inserted into the math field. Wraps rather
   than scrolls, so no row is hidden at a laptop-height viewport where the modal is
   already competing with the field and the actions row for vertical space. */
.equation-structures {
  display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 10px;
  padding-bottom: 10px; border-bottom: 1px solid var(--border);
}
.equation-structure {
  min-width: 34px; min-height: 34px; padding: 3px 7px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text); background: var(--surface2);
  border: 1px solid var(--border); border-radius: 4px; cursor: pointer;
}
.equation-structure:hover { background: var(--accent-light, var(--surface)); border-color: var(--accent); }
/* The face is KaTeX output. Shrink it to button scale and stop it stretching the row:
   a 2x2 matrix is three times the height of a fraction at the same font size. */
.equation-structure .katex { font-size: 0.95em; }
.equation-structure .katex-display { margin: 0; }
.tb-btn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.tb-btn.primary:hover { background: var(--accent-hover, var(--accent)); }
#equation-modal .modal { max-width: 720px; width: 90vw; }

/* Insert Table modal — paste cells, edit a grid, emit longtable LaTeX. */
.insert-table-card { min-width: 640px; max-width: 920px; width: 90vw; max-height: 86vh; }
.insert-table-header-actions { display: flex; align-items: center; gap: 8px; }
.insert-table-paste-zone {
  border: 1px dashed var(--border); border-radius: 6px;
  padding: 10px 12px; min-height: 32px; margin-bottom: 12px;
  font-size: 13px; color: var(--text-dim); background: var(--surface2);
  outline: none;
}
.insert-table-paste-zone[data-empty="false"] { color: var(--text); }
.insert-table-paste-zone:focus { border-color: var(--accent); }
.insert-table-toolbar {
  display: flex; gap: 18px; align-items: center; margin-bottom: 10px; flex-wrap: wrap;
}
.insert-table-cell-fmt { display: flex; gap: 4px; }
.insert-table-fmt-btn {
  width: 26px; height: 26px; border: 1px solid var(--border); background: var(--surface2);
  border-radius: 4px; cursor: pointer; font-size: 13px; line-height: 1; color: var(--text);
  padding: 0; display: inline-flex; align-items: center; justify-content: center;
}
.insert-table-fmt-btn:hover { background: var(--surface); }
.insert-table-fmt-btn:active { transform: translateY(1px); }
.insert-table-col-align {
  width: 100%; padding: 2px; border: 1px solid var(--border); border-radius: 3px;
  background: var(--surface); font-size: 11px; margin-bottom: 2px; color: var(--text);
}
.insert-table-col-width {
  width: 100%; padding: 2px; border: 1px solid var(--border); border-radius: 3px;
  background: var(--surface); font-size: 11px; margin-bottom: 2px; color: var(--text);
  text-align: right;
}
.insert-table-layout-toolbar { font-size: 12px; }
.insert-table-radio-group {
  border: 1px solid var(--border); border-radius: 4px; padding: 2px 8px 4px;
  display: flex; gap: 10px; align-items: center;
}
.insert-table-radio-group legend { font-size: 11px; color: var(--text-dim); padding: 0 4px; }
.insert-table-radio-group label { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.insert-table-width-hint { color: var(--text-dim); font-size: 11px; }
/* Grid-borders preview: when the user picks "Full grid" we set this class on
   the grid so the modal preview mirrors what the longtable output will
   look like in PDF/DOCX. The default already shows borders on every cell,
   so the "rules" variant is what needs to peel them back. */
.insert-table-grid.borders-rules td:not(.insert-table-row-ctrls),
.insert-table-grid.borders-rules th:not(.insert-table-col-ctrls):not(.insert-table-row-ctrls) {
  border-left-color: transparent; border-right-color: transparent;
}
.insert-table-toggle { font-size: 13px; display: flex; align-items: center; gap: 6px; cursor: pointer; }
.insert-table-caption-label { font-size: 13px; display: flex; align-items: center; gap: 6px; flex: 1; min-width: 240px; }
.insert-table-caption-label input { flex: 1; padding: 4px 8px; border: 1px solid var(--border); border-radius: 4px; font: inherit; }
.insert-table-grid-wrap { overflow: auto; max-height: 48vh; border: 1px solid var(--border); border-radius: 6px; }
.insert-table-grid { border-collapse: collapse; width: 100%; font-size: 13px; }
.insert-table-grid th, .insert-table-grid td {
  border: 1px solid var(--border-strong); padding: 6px 8px; min-width: 80px; vertical-align: top;
  outline: none; position: relative;
}
/* Excel-style header labels: column letters in the col-controls row,
   row numbers in the row-controls column. Small, dim text so they don't
   visually compete with the editable cell contents. */
.insert-table-col-letter {
  font-size: 11px; font-weight: 600; color: var(--text-dim);
  text-align: center; margin-bottom: 2px; letter-spacing: 0.5px;
}
.insert-table-row-number {
  font-size: 11px; font-weight: 600; color: var(--text-dim);
  text-align: center; margin-bottom: 2px;
}
/* Corner label cell (top-left). Vertical rhythm matches each col-ctrl
   cell's stacked inputs: the spacer block matches the column letter row,
   then "Align" lines up with the alignment dropdown, "Width %" lines up
   with the width input (when shown). The labels are right-aligned so they
   read as captions for the inputs immediately to their right. */
.insert-table-corner-labels { vertical-align: top; padding: 2px 6px !important; }
.insert-table-corner-letter-spacer {
  font-size: 11px; height: 14px; visibility: hidden;
}
.insert-table-corner-label {
  font-size: 11px; color: var(--text-dim); text-align: right;
  margin-bottom: 2px; line-height: 1.6em;
}
/* Header cells: background-only cue. Force `font-weight: normal` to
   override the user-agent stylesheet (every browser bolds `<th>` by
   default). With the computed weight being bold, `execCommand('bold')`
   inside a `<th>` interprets the click as a toggle-off and injects
   `<span style="font-weight: normal">` instead of the intended `<b>`.
   Pandoc + the reference docx styles the header row bold on export
   anyway, so the visual matches at export time. */
.insert-table-grid th { background: var(--surface2); font-weight: normal; }
.insert-table-grid td:focus, .insert-table-grid th:focus { background: var(--accent-light, #fdf3ee); }
.insert-table-col-ctrls { background: transparent !important; border-bottom: none !important; padding: 2px !important; }
.insert-table-row-ctrls { background: transparent !important; border-right: none !important; padding: 2px !important; }
.insert-table-icon-btn {
  width: 20px; height: 20px; border: 1px solid var(--border); background: var(--surface2);
  border-radius: 3px; cursor: pointer; font-size: 12px; line-height: 1; color: var(--text-dim);
  padding: 0; display: inline-flex; align-items: center; justify-content: center;
}
.insert-table-icon-btn:hover { color: var(--text); background: var(--surface); }
.insert-table-icon-btn.danger:hover { color: #b3261e; }
.insert-table-ctrl-row { display: flex; gap: 4px; justify-content: center; }
.insert-table-hint { font-size: 12px; color: var(--text-dim); margin-top: 8px; }
.patch-view-modal-card { max-width: 900px; width: 90vw; }
.patch-view-text {
  background: var(--code-bg);
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 4px;
  padding: 16px;
  font-family: 'Menlo', monospace;
  font-size: 13px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-wrap: break-word;
  max-height: 65vh;
  overflow-y: auto;
  margin: 0 0 12px 0;
}
/* Lift the MathLive virtual keyboard above the equation modal backdrop
   (z-index 200) so its keys remain clickable while the modal is open. */
:root { --keyboard-zindex: 250; }
body > .ML__keyboard { z-index: var(--keyboard-zindex); }

.media-toolbar {
  display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
}
.media-hint { font-size: 12px; color: var(--text-dim); }

.media-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.media-item {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 10px; border: 1px solid var(--border);
  border-radius: 4px; background: var(--surface2);
}
.media-item.drag-over { border-color: var(--accent); background: var(--accent-light); }
.media-thumb {
  width: 48px; height: 48px; object-fit: contain;
  background: #fff; border: 1px solid var(--border); border-radius: 3px;
  flex-shrink: 0;
}
.media-thumb-fallback {
  width: 48px; height: 48px; display: flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--border);
  font-size: 10px; color: var(--text-dim); border-radius: 3px; flex-shrink: 0;
}
.media-name {
  flex: 1; font-family: 'Menlo', monospace; font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.media-drag-handle {
  cursor: grab; color: var(--text-dim); font-size: 16px;
  padding: 0 4px; user-select: none;
}
.media-drag-handle:active { cursor: grabbing; }
.media-order {
  width: 48px; padding: 2px 4px; font-size: 12px;
  border: 1px solid var(--border); border-radius: 3px; background: var(--surface);
  font-family: inherit;
}
.media-actions {
  display: flex; gap: 4px;
}
.media-actions button {
  padding: 3px 8px; font-size: 12px; border-radius: 3px;
  background: var(--surface); border: 1px solid var(--border);
  color: var(--text); cursor: pointer; font-family: var(--ui-font);
}
.media-actions button:hover { background: var(--accent-light); border-color: var(--accent); color: var(--accent); }
.media-actions .btn-delete:hover { background: #fbe9e9; border-color: #c44; color: #a33; }
.media-empty { color: var(--text-dim); font-style: italic; padding: 20px 0; text-align: center; }

/* Clickable thumbnails in the media manager */
.media-thumb { cursor: zoom-in; transition: transform 0.1s; }
.media-thumb:hover { transform: scale(1.05); }

/* Lightbox overlay */
.lightbox-backdrop {
  position: fixed; top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.85);
  display: flex; align-items: center; justify-content: center;
  z-index: 400; flex-direction: column; gap: 12px;
  padding: 40px;
}
.lightbox-backdrop.hidden { display: none; }
.lightbox-img {
  max-width: 90vw; max-height: 80vh;
  background: #fff; border-radius: 4px;
  box-shadow: 0 10px 50px rgba(0,0,0,0.5);
}
.lightbox-caption {
  color: #fff; font-family: 'Menlo', monospace; font-size: 13px;
  text-align: center; opacity: 0.9;
}
.lightbox-close {
  position: absolute; top: 16px; right: 20px;
  background: none; border: none; color: #fff; font-size: 32px;
  cursor: pointer; line-height: 1; padding: 4px 10px;
  border-radius: 4px;
}
.lightbox-close:hover { background: rgba(255,255,255,0.15); }

/* Table preview modal — opened by clicking a Clean-view table chip. Reuses
   .lightbox-backdrop for the dim overlay; the inner .table-preview-card is a
   white panel that holds a real HTML <table> rendered from the parsed LaTeX. */
.table-preview-card {
  background: #fff; border-radius: 6px;
  box-shadow: 0 10px 50px rgba(0,0,0,0.5);
  max-width: 92vw; max-height: 86vh;
  overflow: auto; padding: 24px 28px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.table-preview-caption {
  font-size: 14px; color: #444; margin-bottom: 12px;
  font-weight: 600; text-align: center;
}
.table-preview-table {
  border-collapse: collapse; margin: 0 auto;
  font-size: 13px;
}
.table-preview-table th,
.table-preview-table td {
  border: 1px solid rgba(0,0,0,0.15);
  padding: 6px 12px; vertical-align: top; text-align: left;
}
.table-preview-table th {
  background: rgba(46, 92, 122, 0.08);
  font-weight: 600; color: #2e5c7a;
}
.table-preview-table tbody tr:nth-child(even) {
  background: rgba(0,0,0,0.02);
}
.table-preview-empty {
  color: #777; font-style: italic; padding: 12px;
}

/* ---------- Round 1: Format menu, View menu, Highlight popover, editor zoom ---------- */

/* Editor text zoom: a CSS variable drives the font size of the CodeMirror content.
   Default 100%. Changed via the View menu, persisted in localStorage. */
:root {
  --editor-zoom: 1;
}

.cm-content, .cm-gutters {
  font-size: calc(13px * var(--editor-zoom)) !important;
  line-height: calc(1.5 * var(--editor-zoom)) !important;
}

/* Section titles inside the Format/View dropdown menus */
.menu-section-title {
  padding: 6px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim, #888);
  border-bottom: 1px solid var(--border-light, #eee);
  margin-bottom: 2px;
}

.menu-note {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-dim, #888);
  font-size: 10px;
  margin-left: 4px;
}

.menu-note-inline {
  padding: 6px 12px;
  color: var(--text-dim, #888);
  font-size: 11px;
  font-style: italic;
}

/* Active-value indicator in Format/View menus: a thin left bar on the
   currently-selected option. Toggled by JS when the menu opens. */
.menu-item.is-active {
  background: var(--accent-bg, #f2e4e0);
  border-left: 3px solid var(--accent, #7a3b2e);
  padding-left: 9px;
}

/* Highlight popover (toolbar child menu) */
.tb-highlight-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.hl-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 2px;
  border: 1px solid rgba(0, 0, 0, 0.2);
  margin-right: 6px;
  vertical-align: middle;
}

/* ---------- Soft-conceal LaTeX rendering (Overleaf-style) ---------- */
/* Decorations applied to the inner content of \textbf, \textit, \emph,
   \hl, and \sethlcolor{c}\hl. Markup tokens themselves remain visible. */

.cm-sc-bold       { font-weight: 700; }
.cm-sc-italic     { font-style: italic; }

/* Source-view LaTeX syntax highlighting (latexSyntaxPass). Colours match the
   reference "Code" view token-for-token. */
.cm-tex-comment   { color: #4c886b; } /* %% comments              — green  */
.cm-tex-command   { color: #0000ff; } /* \commands, \\            — blue   */
.cm-tex-literal   { color: #833fba; } /* \{ \% \& escapes         — purple */
.cm-tex-option    { color: #318495; } /* [optional] values        — teal   */
.cm-tex-math      { color: #036a07; } /* $math$ text, $, numbers  — green  */
.cm-tex-punct     { color: #000000; } /* { } braces inside math   — black  */

/* Source ("Code") view chrome — white page + light gutter, like the
   reference editor. Scoped to Source mode (cm-cw-source) so Clean view's
   paper styling is untouched. */
.cm-editor.cm-cw-source { background: #ffffff; }
.cm-editor.cm-cw-source .cm-gutters { background: #f0f0f0; color: #333333; border-right: 1px solid #e6e6e6; }
.cm-editor.cm-cw-source .cm-lineNumbers .cm-gutterElement { color: #333333; }

.cm-sc-hl-yellow  { background-color: rgba(255, 224, 102, 0.55); }
.cm-sc-hl-green   { background-color: rgba(182, 230, 182, 0.55); }
.cm-sc-hl-pink    { background-color: rgba(247, 184, 209, 0.55); }
.cm-sc-hl-blue    { background-color: rgba(182, 212, 236, 0.55); }
.cm-sc-hl-default { background-color: rgba(255, 224, 102, 0.55); }

/* ---------- Clean view ---------- */
/* Heading marks (`\section{}` / `\subsection{}` / `\subsubsection{}`) now
   render the inner text identically to body text in Clean view — the marker
   tokens are still hidden by softConcealPass, so the user sees plain text
   while the LaTeX source retains the section structure for outline + PDF.
   The classes stay so the soft-conceal pass has something non-null to apply. */
.cm-editor-cite-popover {
  position: absolute; z-index: 9999; min-width: 240px; max-width: 340px;
  max-height: 62vh; overflow-y: auto;
  background: #fff; border: 1px solid rgba(0, 0, 0, 0.18); border-radius: 4px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12); padding: 8px 10px;
  font-family: var(--ui-font); font-size: 13px;
}
.cm-editor-cite-popover-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-dim, #888); margin-bottom: 6px;
  font-family: 'Courier New', monospace;
}
.cm-editor-cite-popover-list { display: flex; flex-direction: column; }
.cm-editor-cite-popover-empty {
  font-size: 12px; color: var(--text-dim, #888); font-style: italic; padding: 2px 0 6px;
}
.cm-editor-cite-popover-row {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 4px 0; font-size: 13px;
}
.cm-editor-cite-popover-info { flex: 1; min-width: 0; }
.cm-editor-cite-popover-label { font-size: 12.5px; color: var(--text, #222); line-height: 1.3; }
.cm-editor-cite-popover-unlinked { font-size: 11px; color: #9a6a00; font-style: italic; }
.cite-popover-row-unlinked .cm-editor-cite-popover-label { color: #9a6a00; }
.cm-editor-cite-popover-link {
  font-size: 12px; padding: 2px 8px; margin-right: 6px; white-space: nowrap; cursor: pointer;
  border: 1px solid var(--accent-mid, #cbd5e1); border-radius: 4px;
  background: var(--bg, #fff); color: var(--accent, #2563eb);
}
.cm-editor-cite-popover-link:hover { background: var(--accent, #2563eb); color: #fff; }
.cm-editor-cite-popover-key {
  font-family: 'Menlo', 'Courier New', monospace; font-size: 10.5px;
  color: var(--text-dim, #888); word-break: break-all; margin-top: 1px;
}
.cm-editor-cite-popover-remove {
  flex: none; font-size: 11px; padding: 2px 8px; cursor: pointer;
  background: #fff; border: 1px solid rgba(0, 0, 0, 0.20); border-radius: 3px;
  color: #a33;
}
.cm-editor-cite-popover-remove:hover {
  background: #fbecec; border-color: #c66;
}
.cm-editor-cite-popover-add {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.10);
}
.cm-editor-cite-popover-addlabel {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-dim, #888); margin-bottom: 4px;
}
.cm-editor-cite-popover-search {
  width: 100%; box-sizing: border-box; padding: 5px 8px; font-size: 12px;
  border: 1px solid rgba(0, 0, 0, 0.20); border-radius: 3px; font-family: var(--ui-font);
}
.cm-editor-cite-popover-results {
  display: flex; flex-direction: column; margin-top: 6px;
  max-height: 210px; overflow-y: auto;
}
.cm-editor-cite-popover-result {
  text-align: left; display: flex; flex-direction: column; gap: 1px;
  padding: 5px 6px; background: #fff; border: none; border-radius: 3px;
  cursor: pointer; font-family: var(--ui-font);
}
.cm-editor-cite-popover-result:hover { background: var(--hover, #f2efe9); }
.cm-editor-cite-popover-result-title {
  font-size: 12px; color: var(--text, #222);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cm-editor-cite-popover-result-meta { font-size: 10.5px; color: var(--text-dim, #888); }
.cm-editor-cite-popover-searching {
  font-size: 11.5px; color: var(--text-dim, #888); font-style: italic; padding: 4px 2px;
}
.bib-insert-popover {
  position: absolute; z-index: 10001; min-width: 250px; max-width: 330px;
  background: #fff; border: 1px solid rgba(0, 0, 0, 0.18); border-radius: 4px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16); padding: 12px 14px;
  font-family: var(--ui-font); font-size: 13px;
}
.bib-insert-title { font-weight: 600; margin-bottom: 6px; color: var(--text, #222); }
.bib-insert-msg { color: var(--text-dim, #666); font-size: 12.5px; line-height: 1.4; margin-bottom: 10px; }
.bib-insert-actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
.bib-insert-btn {
  padding: 5px 12px; font-size: 12.5px; cursor: pointer;
  background: #fff; border: 1px solid rgba(0, 0, 0, 0.22); border-radius: 3px;
  color: var(--text, #222); font-family: var(--ui-font);
}
.bib-insert-btn:hover { background: var(--hover, #f2efe9); }
.bib-insert-btn-primary { background: var(--accent, #7a3b2e); color: #fff; border-color: var(--accent, #7a3b2e); }
.bib-insert-btn-primary:hover { opacity: 0.9; background: var(--accent, #7a3b2e); }
.cm-bib-citekey { font-family: 'Menlo', monospace; font-size: 0.9em; }
.cm-bib-missing { color: var(--text-dim, #888); font-style: italic; }
/* Word-style movable icon chip for figures and tables in Clean view. The
   chip is an inline-block draggable span. The full LaTeX block (figure /
   center+image / longtable / tabular) is hidden behind it via
   Decoration.replace; the user moves the chip and the underlying source
   range moves with it as a single CM6 dispatch. */
/* Word-style figure preview: the actual image rendered inline at the same
   body-width fraction it will occupy on the PDF page. A right-edge handle
   lets the user drag-resize; the readout floats next to the handle and
   shows the current body-width percentage during the gesture. The wrap is
   a `display: block` element so it can sit on its own line within an
   inline `Decoration.replace`; `margin: 0 auto` centers it like the
   `\begin{center}…\end{center}` block does in the PDF. */
/* Inline table grid. The wrapper stacks a chip-style header strip (label,
   caption, edit button, drag handle) on top of an HTML <table> that renders
   the parsed longtable at its source-declared column proportions. The
   strip preserves all the drag-to-reorder and open-in-editor affordances
   the legacy chip had, so the inline grid is strictly additive. */
/* Grid body is the absolute-positioning context for the column-edge drag
   handles, so they can be placed at percent-of-body x-positions and span
   the full table height. */
/* Column-edge drag handle: a 6px-wide invisible hit zone centered on the
   column boundary. A 1px tinted bar in the middle shows on hover so the
   user can see where the handle is. Spans the table's full height via
   top: 0; bottom: 0 — the body's relative positioning gives the right
   reference frame. */
/* Page-boundary widget for paginated Clean view. PageBreakWidget is an
   INLINE widget rendered as a full-width flex column, so it sits inside the
   paragraph's line box and splits the paragraph across the page boundary
   (the leading lines render above it, the rest below). Three stacked
   children, each with a fixed inline `height` set by the widget:

   - paper-bottom: white, the outgoing page's bottom margin. Hosts the folio.
   - gutter: canvas-gray, bleeds past page edges via the existing
     --editor-landscape-extra-pct negative margins. Hosts the chip.
   - paper-top: white, the incoming page's top margin.

   The widget never pads to fill a page, so there is no blank-void state and
   no measurement phase. Multi-page (collapsed) groups carry
   `data-page-end > data-page-start` and a "Pages N–M" chip label. */
/* Mid-paragraph (inline) dividers render inline-flex so the wrapped row before
   them is justified flush to the margin like every other line. A block-level
   divider makes that row the "last line" of its inline segment, which
   text-align: justify leaves ragged — visibly short of the right margin. As an
   inline atom the divider wraps like a normal word, so the preceding line is a
   normal justified wrap. vertical-align: top keeps it off the text baseline. */
/* Folio: the outgoing page number, sitting in the bottom-margin band of
   the page that just ended (the paper-bottom region of the widget). */
/* Canvas-gray gutter bleeds past the body width using the same negative-
   margin proportion the landscape block uses (--editor-landscape-extra-pct
   = margin / body-width, exactly what's needed to reach the page's outer
   edge). Subtle inset shadows at top and bottom give a paper-edge feel. */
/* Per-row drag handle: a thin horizontal hit zone at the bottom edge of each
   tbody row. Invisible by default; a 1px tinted bar shows on hover so the
   user knows where the handle is. Drag vertically to adjust that row's
   trailing space (`\\[Xem]` in source). Sits absolutely inside the body
   wrapper at `top: <row.bottom - body.top - 3>`. */
/* Drop indicator: a thin bar shown while a chip is being dragged.
   Block-chip drag uses the horizontal variant (a line spanning the editor
   width above the destination line). Inline-chip drag (cite, math) uses the
   vertical variant — a caret positioned at the character-precise drop
   location. Positioned absolutely inside the editor's scroll container. */
/* Clean view defaults to justified text — matches LaTeX's default body
   alignment so a paragraph that compiles justified in the PDF also
   *renders* justified in Clean view. Per-paragraph alignment overrides
   come from the line decorations added by buildCleanDecorations when the
   source contains \begin{center|flushleft|flushright}. The override
   selectors carry an extra class to outrank the default's specificity. */
/* CodeMirror's default highlightActiveLine extension paints the active line
   with a pale-blue background. In Clean view, a paragraph is one logical
   line that wraps across many visual rows, so the highlight covers the
   whole paragraph and reads as "this paragraph is selected/highlighted."
   Suppress it in Clean view; Source view keeps the indicator. */
#editor.is-editor-view .cm-content .cm-line.cm-activeLine,
#editor.is-editor-view .cm-content .cm-activeLine { background-color: transparent; }
#editor.is-editor-view .cm-content .cm-line                          { text-align: justify; }
/* Quote envs: 0.5 ruler-inch indent on each side. The ruler stretches the
   document page (default 8.5in) across the editor width, so its "inch" is
   editor-width-relative, not the CSS-screen-inch (96px). Using `0.5in` here
   would yield 48px regardless of editor width, which mismatches the ruler.
   --quote-indent-pct is set by renderCleanRuler() from the parsed page
   geometry (0.5in / body width × 100). The 7.69% fallback matches default
   8.5in × 11in with 1in margins (= 0.5/6.5 × 100). */
/* List items: marker sits flush with the body-text left edge (Word-style),
   item content begins 0.25 ruler-inch past it, wrapped lines align under
   the content. CSS-em units would diverge from the ruler scale on resize;
   --ruler-inch-pct keeps the indent proportional to the document's body
   width regardless of editor width. The +6px padding pairs with CodeMirror's
   default .cm-line padding so the marker lines up with body-paragraph text. */
/* Word-like paragraph flow (see paragraphFlowPass). The first-line indent is
   an inline-style line decoration sized to the document's \parindent, so it is
   not a static rule here. This rule collapses the blank line pandoc inserts
   between two paragraphs so they sit together as in Word. */
/* Math symbol picker popover (Overleaf-style: tabs + grid). Anchored under
   the toolbar button via tb-menu-wrap's relative positioning. */
.symbol-popover {
  position: absolute; top: calc(100% + 4px); left: 0;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 5px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);
  z-index: 1200; padding: 8px; width: 360px;
}
.symbol-popover.hidden { display: none; }
.symbol-tabs { display: flex; gap: 2px; flex-wrap: wrap; margin-bottom: 6px; }
.symbol-tab {
  padding: 4px 8px; font-size: 11px; cursor: pointer;
  background: transparent; border: 1px solid transparent; border-radius: 3px;
  color: var(--text-dim, #666);
}
.symbol-tab:hover { background: rgba(122, 59, 46, 0.08); }
.symbol-tab.active { color: var(--accent); border-color: var(--accent-mid); background: rgba(122, 59, 46, 0.06); }
.symbol-grid {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px;
  max-height: 280px; overflow-y: auto;
}
.symbol-cell {
  padding: 6px 4px; cursor: pointer; text-align: center;
  font-size: 16px; line-height: 1.2;
  background: transparent; border: 1px solid transparent; border-radius: 3px;
  color: var(--text, #333); font-family: 'STIX Two Math', 'Cambria Math', 'Latin Modern Math', serif;
  user-select: none;
}
.symbol-cell:hover { background: rgba(122, 59, 46, 0.08); border-color: var(--accent-mid); }
.symbol-hint { font-size: 11px; color: var(--text-dim, #888); margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border, #ddd); }

/* Edit → Paste from Markdown. The textarea is monospaced because the user is looking at
   Markdown, where the leading characters of a line carry the meaning. */
.markdown-paste-card { width: 620px; max-width: 94vw; }
.markdown-paste-card .modal-body { padding: 14px 16px; }
.markdown-paste-intro { margin: 0 0 10px; font-size: 13px; color: var(--text-dim); line-height: 1.5; }
.markdown-paste-intro code { font-family: 'Menlo', 'Courier New', monospace; font-size: 12px; }
.markdown-paste-input {
  width: 100%; box-sizing: border-box; resize: vertical;
  padding: 9px 10px; font-size: 13px; line-height: 1.55;
  font-family: 'Menlo', 'Courier New', monospace;
  border: 1px solid var(--border); border-radius: 4px;
  background: var(--surface); color: var(--text);
}
.markdown-paste-input:focus { outline: none; border-color: var(--accent-mid, #b8837a); }

/* Tools → Word count. A figures table, so the numbers are right-aligned and tabular-figured:
   comparing a document column against a selection column is the whole point of the dialog. */
.word-count-card { width: 420px; max-width: 92vw; }
.word-count-card .modal-body { padding: 14px 16px; }
.word-count-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.word-count-table th, .word-count-table td { padding: 7px 4px; border-bottom: 1px solid var(--border); }
.word-count-table thead th { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--text-dim); font-weight: 600; }
.word-count-table tbody th { text-align: left; font-weight: 400; color: var(--text-dim); }
.word-count-table td { text-align: right; font-variant-numeric: tabular-nums; }
.word-count-table thead th:not(:first-child) { text-align: right; }
.word-count-table tr:last-child th, .word-count-table tr:last-child td { border-bottom: none; }
.word-count-note { margin: 10px 0 0; font-size: 12px; color: var(--text-dim); }

/* Insert → Special characters. Reuses the maths picker's tabs and grid so the two read as one
   family; only the sizing differs, because prose glyphs are picked from a much longer list and
   the dialog is searched rather than scanned. The cells override the maths font: an accented
   letter should render in the UI face the user will actually see it in, not in a maths face. */
.special-char-card { width: 560px; max-width: 94vw; }
.special-char-card .modal-body { padding: 14px 16px; }
.special-char-card .symbol-grid { grid-template-columns: repeat(12, 1fr); max-height: 46vh; gap: 3px; }
.special-char-card .symbol-cell { font-family: var(--ui-font); font-size: 18px; padding: 8px 4px; }
.special-char-card .symbol-cell.is-blank { color: var(--text-dim); font-size: 15px; }
.special-char-search {
  width: 100%; box-sizing: border-box; margin-bottom: 10px;
  padding: 7px 9px; font-size: 14px; border: 1px solid var(--border);
  border-radius: 4px; background: var(--surface); color: var(--text); font-family: var(--ui-font);
}
.special-char-search:focus { outline: none; border-color: var(--accent-mid, #b8837a); }
.special-char-empty { grid-column: 1 / -1; margin: 12px 4px; font-size: 13px; color: var(--text-dim); }

/* Inline / display math widgets (KaTeX-rendered). The KaTeX stylesheet (loaded
   in index.html) provides the .katex / .katex-display classes; we just adjust
   spacing so the rendered span sits naturally inline with body text. */
/* Clean view ruler: a Word-style horizontal rule above the editor that shows
   inches (US Letter assumed unless geometry overrides) with margin shading
   pulled from \usepackage[margin=...]{geometry}. Only visible in Clean view;
   informational, not interactive. */
.editor-ruler {
  position: relative;
  height: 22px;
  background: #faf7f3;
  border-bottom: 1px solid #e6dccc;
  user-select: none;
  overflow: hidden;
  flex-shrink: 0;
}
.editor-ruler.hidden { display: none; }
.editor-ruler .ruler-margin {
  position: absolute;
  top: 0; bottom: 0;
  background: rgba(122, 59, 46, 0.10);
}
.editor-ruler .ruler-tick {
  position: absolute;
  bottom: 0;
  width: 1px;
  height: 4px;
  background: rgba(60, 40, 30, 0.35);
}
.editor-ruler .ruler-tick-mid   { height: 7px; }
.editor-ruler .ruler-tick-major { height: 10px; background: rgba(60, 40, 30, 0.6); }
.editor-ruler .ruler-label {
  position: absolute;
  top: 2px;
  transform: translateX(-50%);
  font-size: 9px;
  color: rgba(60, 40, 30, 0.7);
  line-height: 1;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* Clean view: render the editor in the user's chosen LaTeX font / size /
   line spacing. Source view inherits the monospace baseline from #editor.
   We target .cm-line as well as .cm-content because CodeMirror 6's base
   theme can set line-height on .cm-line directly, which would otherwise
   mask our --editor-line-height variable. */
#editor.is-editor-view .cm-scroller,
#editor.is-editor-view .cm-content,
#editor.is-editor-view .cm-content .cm-line {
  font-family: var(--editor-font-family, Georgia, serif);
  /* Zoom multiplier composes with the Format-menu base size so Clean view
     responds to View → Zoom (the source/PDF-tab base rule already does).
     !important is required to beat the base `.cm-content … !important` 13px
     rule (line ~1261); this selector is more specific, so among !important
     declarations it wins — giving Clean view the true print size/line-pitch. */
  font-size: calc(var(--editor-font-size, 15px) * var(--editor-zoom, 1)) !important;
  line-height: var(--editor-line-height, 1.5) !important;
}
/* Print-faithful line-breaking: hyphenate justified prose like LaTeX does, so
   wrap points (and thus line counts per page) track the PDF more closely. Not
   identical to TeX's hyphenation patterns — the residual ±1-2 line ceiling. */
#editor.is-editor-view .cm-content .cm-line {
  -webkit-hyphens: auto; hyphens: auto;
  text-rendering: optimizeLegibility;
}

/* Clean view: drop the source-line gutter (line numbers + fold arrows). They
   map to LaTeX source lines, not the visible reading lines, so they confuse
   more than they help in a Word-like view. */
#editor.is-editor-view .cm-gutters { display: none !important; }

/* Clean view: shrink the content area to the ruler's unshaded zone so the
   text physically sits between the margins shown by #editor-ruler. The
   percentage is set by renderCleanRuler() from \usepackage[margin=...]{geometry}. */
#editor.is-editor-view .cm-content {
  padding-left:  var(--editor-margin-pct, 12%);
  padding-right: var(--editor-margin-pct, 12%);
}

/* ---------- Paper-frame (Word-style) ---------- */
/* Decorative page chrome only — Clean view still flows continuously through
   a single CodeMirror instance, but the cm-content now looks like a stacked
   sheet of paper sitting on a gray canvas. The page-break widget (already
   inserted at SyncTeX / heuristic break positions by clean-view.js) provides
   the visual gap between "pages". This is theatre: content does not
   physically fit a page sheet, so a paragraph long enough to overflow a
   real page will just keep flowing past where the PDF would break. The PDF
   tab remains the authoritative print-layout view. */

/* Gray canvas around the page sheet. With the per-page sheet overlay
   painting individual white rectangles, the editor pane shows through as
   the canvas BETWEEN sheets (the gutter zone). Editor and scroller bg
   remain transparent so the pane's color is the single source of truth. */
#editor-view .editor-pane:has(#editor.is-editor-view) {
  background: #e6e3dc;
}
#editor.is-editor-view {
  background: #e6e3dc;
}
#editor.is-editor-view .cm-scroller {
  background: transparent;
}

/* Constrain the ruler to the page width so it sits directly above the
   paper sheet rather than spanning the entire pane. `width: 100%` is needed
   alongside `margin: 0 auto` because in a flex column container, auto
   cross-axis margins override the default `align-items: stretch`; without an
   explicit width the ruler collapses to its intrinsic width of zero (its
   children are absolutely positioned and contribute no width). */
#editor-view .editor-pane:has(#editor.is-editor-view) .editor-ruler {
  max-width: var(--editor-page-width-px, 100%);
  width: 100%;
  margin: 0 auto;
}

/* The CM6 content. In path D1 the sheet overlay (below) paints the actual
   white sheets per PDF page; cm-content's own background is transparent so
   it doesn't double-paint over the sheet rectangles. max-width widens to
   --editor-canvas-width-px when any page in the doc is landscape, so the
   editor pane reserves room for the wider landscape sheet. */
/* The white paper. A single continuous cm-content element with an opaque
   white background — paper is ALWAYS visible with zero JS dependency.
   Centred via margin:0 auto inside the gray #editor canvas so the canvas
   shows on the left/right; the page-break widgets interrupt the white
   with gray gutter bands to separate pages. max-width widens to
   --editor-canvas-width-px when the doc has landscape pages. */
#editor.is-editor-view .cm-content {
  max-width: var(--editor-canvas-width-px, var(--editor-page-width-px, 100%));
  margin: 0 auto;
  background: #ffffff;
  padding-top:    var(--editor-page-pad-vert, 1in);
  padding-bottom: var(--editor-page-pad-vert, 1in);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.06),
    0 2px 6px rgba(0, 0, 0, 0.08),
    0 12px 28px rgba(0, 0, 0, 0.10);
}

/* Vertical ruler: pinned to the left edge of #editor (parent of CM6's
   .cm-editor). Stays fixed in viewport space; the painter writes tick /
   label / margin div positions in viewport coordinates derived from the
   scrollTop of the current sheet. */
#editor {
  position: relative;
}
#editor > .clean-vruler {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 22px;
  background: #f3ecd9;
  border-right: 1px solid rgba(60, 40, 30, 0.18);
  z-index: 6;
  pointer-events: none;
  overflow: hidden;
}
#editor:not(.is-editor-view) > .clean-vruler {
  display: none;
}
.clean-vruler-track {
  position: relative;
  width: 100%;
  height: 100%;
}
/* Top/bottom page-margin shading — the vertical analog of the horizontal
   ruler's .ruler-margin, same tint so borders read identically on both axes. */
.clean-vruler .vruler-margin {
  position: absolute;
  left: 0;
  right: 0;
  background: rgba(122, 59, 46, 0.10);
}
.clean-vruler .vruler-tick {
  position: absolute;
  right: 0;
  width: 4px;
  height: 1px;
  background: rgba(60, 40, 30, 0.35);
}
.clean-vruler .vruler-tick.ruler-tick-mid {
  width: 7px;
}
.clean-vruler .vruler-tick.ruler-tick-major {
  width: 10px;
  background: rgba(60, 40, 30, 0.6);
}
.clean-vruler .vruler-label {
  position: absolute;
  right: 12px;
  font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif;
  font-size: 9px;
  color: rgba(60, 40, 20, 0.55);
  transform: translateY(-50%);
}
/* CM6 draws the selection rectangles inside .cm-selectionLayer at z-index -2
   (set INLINE on the element by CM6's runtime), intended for a transparent
   .cm-content. Our paper sheet paints opaque white over that layer, hiding
   the highlight. Override the inline z-index with !important so the layer
   sits ABOVE cm-content, and tint the rectangles with a translucent blue so
   the user sees a Word-style highlight while the underlying text remains
   readable through the overlay. CRUCIAL: also set pointer-events: none — CM6
   only sets it on .cm-cursorLayer because the selection layer normally sits
   at z:-2 (below everything) and never catches mouse events; once we lift it
   above cm-content, the overlay otherwise intercepts mousedown on highlighted
   text, blocking the drag-from-selection gesture entirely. */
#editor.is-editor-view .cm-selectionLayer {
  z-index: 1 !important;
  pointer-events: none;
}
#editor.is-editor-view .cm-selectionBackground,
#editor.is-editor-view .cm-content ::selection {
  background-color: rgba(80, 130, 220, 0.28);
}

/* Landscape page markers — block widgets at each `\begin{landscape}…\end{
   landscape}` boundary. Reverted from the per-line band styling (R2 trial
   2026-05-19) because CM6's class-merge for `Decoration.line` silently
   drops landscape classes under common conditions (cursor-on-line via
   highlightActiveLine, post-edit re-renders, prelude-hide co-location),
   making the band visually vanish for individual landscapes. Block
   widgets render reliably independent of cm-line decoration. */
/* Pandoc `\noindent ` hide — implemented as a mark (not a replace) so it
   doesn't co-locate with line decorations at line.from and silently drop
   them. Hide via font-size + line-height collapse; keeps the element in
   layout for CM6 caret measurement without contributing visible space. */
/* ---------- Fold placeholder (longtable etc.) ---------- */
.cm-foldPlaceholder {
  background: rgba(122, 59, 46, 0.08);
  border: 1px solid rgba(122, 59, 46, 0.25);
  color: rgba(122, 59, 46, 0.85);
  padding: 1px 8px;
  border-radius: 3px;
  font-size: 0.9em;
  cursor: pointer;
}

/* Fold gutter arrows: make them visible and obviously clickable. */
.cm-foldGutter {
  min-width: 14px;
}
.cm-foldGutter .cm-gutterElement {
  cursor: pointer;
  color: rgba(122, 59, 46, 0.55);
  font-size: 1.05em;
  padding: 0 3px;
  user-select: none;
}
.cm-foldGutter .cm-gutterElement:hover {
  color: rgba(122, 59, 46, 1);
}

/* Inline "collapse" chip rendered after \begin{figure}/\begin{longtable} */
.cm-collapse-chip {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  font-size: 0.8em;
  font-weight: 500;
  background: rgba(122, 59, 46, 0.12);
  border: 1px solid rgba(122, 59, 46, 0.35);
  border-radius: 3px;
  color: rgba(122, 59, 46, 0.9);
  cursor: pointer;
  user-select: none;
  vertical-align: middle;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.cm-collapse-chip:hover {
  background: rgba(122, 59, 46, 0.28);
  border-color: rgba(122, 59, 46, 0.6);
  color: rgba(122, 59, 46, 1);
}

/* ---------- Inline comments ---------- */
.cm-comment-anchor {
  background-color: rgba(255, 200, 100, 0.18);
  border-bottom: 1.5px dotted rgba(200, 130, 50, 0.7);
  cursor: pointer;
}
.cm-comment-anchor:hover {
  background-color: rgba(255, 200, 100, 0.32);
}

.cm-comment-context-menu {
  position: fixed;
  background: white;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 4px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  padding: 4px 0;
  min-width: 150px;
  z-index: 9999;
  font-size: 13px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.cm-comment-context-menu .menu-item {
  padding: 6px 14px;
  cursor: pointer;
  user-select: none;
}
.cm-comment-context-menu .menu-item:hover {
  background: rgba(122, 59, 46, 0.10);
}
.cm-comment-context-menu .menu-subhead {
  padding: 4px 14px 2px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  user-select: none;
}
.cm-comment-context-menu .menu-synonym-status {
  padding: 4px 14px 6px;
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
  user-select: none;
}
.cm-comment-context-menu .menu-synonym { text-transform: capitalize; }

:where(.cm-comment-tooltip, .cm-proposal-tooltip) {
  background: white;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
  padding: 8px 12px;
  max-width: 360px;
  font-size: 13px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.cm-comment-tooltip .cm-comment-author {
  font-size: 11px;
  color: var(--text-dim, #888);
  margin-bottom: 4px;
}
.cm-comment-tooltip .cm-comment-body {
  color: var(--text, #2a2a2a);
  margin-bottom: 8px;
  white-space: pre-wrap;
  line-height: 1.4;
}
.cm-comment-tooltip .cm-comment-actions {
  display: flex;
  gap: 6px;
}
.cm-comment-tooltip .cm-comment-edit,
.cm-comment-tooltip .cm-comment-delete {
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  padding: 3px 10px;
  cursor: pointer;
  font-size: 11px;
  color: var(--text, #2a2a2a);
}
.cm-comment-tooltip .cm-comment-edit:hover,
.cm-comment-tooltip .cm-comment-delete:hover {
  background: rgba(122, 59, 46, 0.10);
  border-color: rgba(122, 59, 46, 0.4);
}

/* ---------- Grammar findings (P1-6) ---------- */
/* The squiggle uses an inline SVG so it stays crisp at any zoom level and
   doesn't depend on font metrics. `text-decoration: wavy` is the simpler
   alternative but renders inconsistently across CodeMirror's line height. */
.cm-grammar-finding {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='6' height='3' viewBox='0 0 6 3'><path d='M0 2 Q1.5 0 3 2 T6 2' fill='none' stroke='%23d63333' stroke-width='1'/></svg>");
  background-repeat: repeat-x;
  background-position: bottom 0 left 0;
  padding-bottom: 1px;
  cursor: pointer;
}
.cm-grammar-tooltip {
  background: white;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
  padding: 10px 12px;
  max-width: 380px;
  font-size: 13px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.cm-grammar-reason {
  font-size: 12px;
  color: var(--text-dim, #888);
  margin-bottom: 8px;
  line-height: 1.4;
}
.cm-grammar-compare {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 10px;
  font-family: var(--ui-font);
  font-size: 13px;
  line-height: 1.4;
}
.cm-grammar-original {
  color: #b33;
  text-decoration: line-through;
  text-decoration-color: rgba(180, 50, 50, 0.5);
}
.cm-grammar-suggestion {
  color: #2d7a2d;
  font-weight: 500;
}
.cm-grammar-actions {
  display: flex;
  gap: 6px;
}
.cm-grammar-accept,
.cm-grammar-dismiss {
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  padding: 4px 12px;
  cursor: pointer;
  font-size: 12px;
  font-family: inherit;
  color: var(--text, #2a2a2a);
}
.cm-grammar-accept {
  background: rgba(45, 122, 45, 0.10);
  border-color: rgba(45, 122, 45, 0.45);
  color: #1f5a1f;
}
.cm-grammar-accept:hover { background: rgba(45, 122, 45, 0.20); }
.cm-grammar-dismiss:hover { background: rgba(0, 0, 0, 0.08); }

/* ---------- Suggesting mode (track-changes layer) ---------- */
.cm-proposal-insert {
  color: var(--proposal-color, #7a3b2e);
  background: color-mix(in srgb, var(--proposal-color, #7a3b2e) 22%, transparent);
  border-bottom: 2px solid var(--proposal-color, #7a3b2e);
  text-decoration: none;
  cursor: pointer;
  padding: 0 1px;
  font-weight: 500;
}
.cm-proposal-insert:hover {
  background: color-mix(in srgb, var(--proposal-color, #7a3b2e) 34%, transparent);
}
.cm-proposal-strike {
  color: var(--proposal-color, #7a3b2e);
  text-decoration: line-through;
  text-decoration-color: var(--proposal-color, #7a3b2e);
  text-decoration-thickness: 2px;
  background: color-mix(in srgb, var(--proposal-color, #7a3b2e) 14%, transparent);
  cursor: pointer;
}
.cm-proposal-strike:hover {
  background: color-mix(in srgb, var(--proposal-color, #7a3b2e) 26%, transparent);
}
/* When a strike is nested inside an insert-range (same author typed it then
   deleted it), CM6 produces nested spans rather than merging classes. Style
   the inner strike so the strikethrough still wins visually and the meaning
   reads as "inserted then retracted" rather than two unrelated annotations. */
.cm-proposal-insert .cm-proposal-strike {
  text-decoration: line-through;
  text-decoration-color: var(--proposal-color, #7a3b2e);
  text-decoration-thickness: 2px;
  background: color-mix(in srgb, var(--proposal-color, #7a3b2e) 28%, transparent);
}
.cm-proposal-tooltip {
  /* Inherits chrome from the .cm-comment-tooltip rule via :where() above. */
  border-left: 3px solid var(--proposal-color, #7a3b2e);
}
.cm-proposal-tooltip .cm-proposal-meta {
  font-size: 11px;
  color: var(--text-dim, #888);
  margin-bottom: 8px;
}
.cm-proposal-tooltip .cm-proposal-actions {
  display: flex;
  gap: 6px;
}
.cm-proposal-tooltip button {
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  padding: 3px 10px;
  cursor: pointer;
  font-size: 11px;
  color: var(--text, #2a2a2a);
}
.cm-proposal-tooltip .cm-proposal-accept:hover {
  background: rgba(46, 122, 70, 0.12);
  border-color: rgba(46, 122, 70, 0.4);
  color: rgb(36, 90, 56);
}
.cm-proposal-tooltip .cm-proposal-reject:hover,
.cm-proposal-tooltip .cm-proposal-withdraw:hover {
  background: rgba(180, 60, 60, 0.10);
  border-color: rgba(180, 60, 60, 0.4);
  color: rgb(135, 40, 40);
}

/* ---------- Auth + user menu + settings/share modals ---------- */
.header-action-btn {
  margin-left: 8px; padding: 4px 12px; font-size: 13px; cursor: pointer;
  background: transparent; color: var(--text);
  border: 1px solid var(--border); border-radius: 4px;
}
.header-action-btn:hover { background: var(--accent-light); border-color: var(--accent-mid); color: var(--accent); }
.header-action-btn.hidden { display: none; }

/* Wider modal for the references panel which needs space for results. */
.modal-wide { max-width: 720px; width: 90vw; }
.modal-header-actions { display: flex; align-items: center; gap: 6px; }

/* The References panel is non-blocking, like the cite popover: the backdrop is transparent and
   lets pointer/scroll events pass through, so the document behind stays scrollable and clickable
   while the panel is open. Scroll to read context, or click into the text to position the cursor,
   then Insert — without the panel closing. The panel re-enables pointer events; close it with the
   × or Escape (click-outside no longer applies, which is what keeps it open while you work behind it). */
#refs-modal.modal-backdrop { background: transparent; pointer-events: none; }
#refs-modal .modal { pointer-events: auto; }

/* ---------- Report a bug ---------- */
/* Non-blocking for the same reason as the References panel, and one more: a bug is
   something the reporter is looking at. A dialog that dims and freezes the app has
   hidden the very thing being described. Here the app stays live behind it, and the
   window can be dragged off the problem by its title bar. */
#bug-modal.modal-backdrop { background: transparent; pointer-events: none; }
#bug-modal .modal { pointer-events: auto; box-shadow: 0 12px 48px rgba(0,0,0,0.3); }
.bug-modal-card { width: 520px; max-width: 92vw; }
/* `grab` promises the drag the header actually performs. The buttons inside it opt
   back out, so the × never looks like a handle. */
.bug-drag-handle { cursor: grab; user-select: none; }
.bug-drag-handle.dragging { cursor: grabbing; }
.bug-drag-handle button { cursor: pointer; }
.bug-intro { font-size: 13px; color: var(--text-dim); margin: 0 0 14px; }
.bug-label { display: block; font-size: 12px; font-weight: 600; margin: 12px 0 5px; color: var(--text); }
.bug-label:first-of-type { margin-top: 0; }
#bug-summary, #bug-details {
  width: 100%; padding: 8px 10px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--border-strong); border-radius: 4px;
  background: var(--surface); color: var(--text); resize: vertical;
}
#bug-summary:focus, #bug-details:focus { outline: 2px solid var(--accent); border-color: var(--accent); }
#bug-summary.err, #bug-details.err { border-color: var(--accent); }
.bug-diag { margin-top: 14px; font-size: 12px; }
.bug-diag > summary { cursor: pointer; color: var(--text-dim); }
.bug-diag > summary:hover { color: var(--text); }
.bug-diag-list {
  margin: 8px 0 0; padding: 10px 12px; border-radius: 4px; background: var(--code-bg);
  display: grid; grid-template-columns: auto 1fr; gap: 4px 12px;
}
.bug-diag-list dt { color: var(--text-dim); }
/* These are machine strings (user agents, ids). Without a break they widen the
   grid column and push the dialog past its own max-width. */
.bug-diag-list dd { margin: 0; word-break: break-word; }
.bug-msg { margin: 12px 0 0; font-size: 13px; color: var(--text-dim); }
.bug-msg:empty { display: none; }
.bug-msg.err { color: var(--accent); }
.bug-footer-hint { font-size: 12px; color: var(--text-dim); }
@media (prefers-color-scheme: dark) {
  .bug-diag-list { background: #1e1c1b; }
}

/* ---------- AI Agent (Claude Code / MCP) panel ---------- */
.agent-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 5px; vertical-align: middle; background: var(--border-strong);
}
.agent-dot[data-state="active"]  { background: #2ea043; box-shadow: 0 0 0 3px rgba(46,160,67,0.18); }
.agent-dot[data-state="idle"]    { background: var(--accent-mid); }
.agent-dot[data-state="waiting"] { background: #e3b341; box-shadow: 0 0 0 3px rgba(227,179,65,0.18); }
.agent-dot[data-state="off"]     { background: var(--border-strong); }

.agent-modal-card { max-width: 560px; width: 92vw; }

.agent-statusline { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.agent-badge {
  font-size: 12px; font-weight: 600; padding: 3px 11px; border-radius: 999px;
  background: var(--border); color: var(--text-dim); white-space: nowrap;
}
.agent-badge[data-state="active"]  { background: #e7f6ea; color: #1a7f37; }
.agent-badge[data-state="idle"]    { background: var(--accent-light); color: var(--accent); }
.agent-badge[data-state="waiting"] { background: #fff4d6; color: #8a6d00; }
.agent-status-detail { font-size: 12.5px; color: var(--text-dim); flex: 1; min-width: 0; }
.agent-test-btn { flex: none; font-size: 11.5px; padding: 3px 10px; white-space: nowrap; }
.agent-troubleshoot {
  margin: 0 0 12px; padding: 9px 11px; font-size: 12px; line-height: 1.5;
  background: #fff8e6; border: 1px solid #f0d98c; border-radius: 6px; color: #6b5900;
}
.agent-troubleshoot code {
  font-size: 11px; background: rgba(0,0,0,0.06); padding: 1px 4px; border-radius: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.agent-desktop-steps { margin: 8px 0 6px; padding-left: 20px; font-size: 12.5px; line-height: 1.6; color: var(--text); }
.agent-desktop-steps li { margin-bottom: 3px; }

.agent-intro { margin: 0 0 4px; font-size: 13.5px; line-height: 1.5; color: var(--text); }

.agent-step { border-top: 1px solid var(--border); padding: 14px 0 4px; }
.agent-step-title { font-weight: 700; font-size: 13.5px; margin-bottom: 4px; }
.agent-step-help { margin: 0 0 8px; font-size: 12.5px; color: var(--text-dim); line-height: 1.45; }
.agent-step-help code, .agent-cando code {
  background: var(--code-bg); padding: 1px 4px; border-radius: 3px; font-size: 11.5px;
}

.agent-token-fresh {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
  padding: 8px 10px; background: #fffbe6; border: 1px solid #e6d98a; border-radius: 6px;
}
.agent-token-fresh code {
  flex: 1; font-size: 11.5px; word-break: break-all; color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.agent-token-list-wrap { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.agent-token-list { list-style: none; margin: 0; padding: 0; width: 100%; }
.agent-token-list li {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 12.5px;
}
.agent-token-list li:last-child { border-bottom: none; }
.agent-token-empty { color: var(--text-dim); font-style: italic; }
.agent-token-meta { color: var(--text-dim); }
.agent-token-revoke {
  background: none; border: 1px solid var(--border-strong); color: var(--text-dim);
  border-radius: 5px; padding: 2px 8px; font-size: 11.5px; cursor: pointer; white-space: nowrap;
}
.agent-token-revoke:hover { border-color: var(--accent); color: var(--accent); }

.agent-cmd-wrap { position: relative; }
.agent-cmd {
  margin: 0; padding: 11px 12px; background: #1c1c1c; color: #e8e6e1;
  border-radius: 6px; font-size: 11.5px; line-height: 1.55;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  /* Wrap, never scroll. These boxes hold one unbroken value (a /mcp URL, or a
     Bearer header carrying a 71-character key), and a scrolling box drew that value
     under the absolutely-positioned Copy button: padding-right reserves space only at
     the END of scrolled content, never on the visible line. Wrapping keeps every line
     clear of the button. The Copy handlers copy the source string, so a visual break
     inside the value never reaches the clipboard. */
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-all;
  padding-right: 76px;
}
/* The Copy button sits ON the dark box, so it needs its own light-on-dark treatment:
   .tb-btn's default is dark text on a transparent background, which rendered it
   invisible here — the one control this box exists to offer. */
.agent-cmd-wrap .tb-btn {
  position: absolute; top: 8px; right: 8px;
  background: rgba(255, 255, 255, 0.14); border: 1px solid rgba(255, 255, 255, 0.3);
  color: #f4f2ef;
}
.agent-cmd-wrap .tb-btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.24); border-color: rgba(255, 255, 255, 0.45); }

/* Step 2 client toggle — Claude Desktop / Claude Code / ChatGPT / connect by URL.
   Four tabs exceed the 560px card on one line, so the row wraps rather than
   overflowing; each tab keeps its full label at every width. */
.agent-client-toggle {
  display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; margin-bottom: 10px;
  max-width: 100%;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
}
.agent-client-tab {
  border: none; background: transparent; cursor: pointer; font: inherit;
  padding: 6px 13px; border-radius: 6px; font-size: 12.5px; font-weight: 600;
  color: var(--text-dim); line-height: 1.2;
}
.agent-client-tab .agent-client-sub { font-weight: 400; opacity: 0.7; font-size: 11px; }
.agent-client-tab.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 2px rgba(0,0,0,0.08); }
.agent-client-tab:not(.active):hover { color: var(--text); }
/* ChatGPT tab: one entry per field of ChatGPT's own form, in the order they appear
   on screen, so the reader can work down both at once. The <dt> is the field's label
   verbatim; the <dd> is what to put in it. */
/* One row per field: the field's own label, a colon, then exactly what goes in it.
   A grid keeps every value starting at the same column, so the reader's eye runs
   straight down the values while the form is open beside them. The label column is
   capped so a long label ("Headers from environment variables") wraps instead of
   squeezing the value, which is the part that has to be read exactly. */
.agent-fieldmap {
  display: grid; grid-template-columns: minmax(6.5em, 11em) minmax(0, 1fr);
  column-gap: 12px; row-gap: 10px; align-items: baseline; margin: 8px 0 10px;
}
.agent-fieldmap dt { margin: 0; font-size: 12.5px; line-height: 1.5; font-weight: 700; color: var(--text); }
.agent-fieldmap dd { margin: 0; min-width: 0; font-size: 12.5px; line-height: 1.5; color: var(--text-dim); }
/* An option to click rather than text to paste: its exact label, without a Copy. */
.agent-field-choice {
  display: inline-block; padding: 2px 7px; font-size: 11.5px; color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--bg); border: 1px solid var(--border); border-radius: 4px;
}
/* A field that must stay blank has nothing to copy, so it reads as a quiet marker. */
.agent-field-empty { color: var(--text-dim); }
/* Create key sits above the key it produces, not after the list of old ones. */
.agent-token-create-row { margin: 6px 0 10px; }
.agent-field-warn { display: block; margin-top: 6px; font-size: 12px; color: var(--text); }
/* Step 4's sample request: set apart as something to adapt, not read as instruction. */
.agent-example {
  margin: 4px 0 10px; padding: 8px 11px; font-size: 12.5px; line-height: 1.5;
  color: var(--text); background: var(--bg); border-left: 3px solid var(--border); border-radius: 4px;
}
/* A field map nested in a numbered step keeps the step's indent rather than adding its own. */
.agent-desktop-steps .agent-fieldmap { margin: 6px 0 4px; }
/* Names the form field a copy box belongs in, where a box stands on its own. */
.agent-field-label { margin: 10px 0 5px; font-size: 12px; font-weight: 600; color: var(--text); }
/* A key made for the app you are NOT looking at: still listed and revocable, but
   visibly secondary so the current app's keys read first. */
.agent-token-other { opacity: 0.55; }

/* The Claude tab's add-on fallback. Folded away because it is the second-choice
   route; it holds the key box while it is open, so it needs real padding. */
.agent-fallback { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px; }
.agent-fallback > summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--text-dim);
  list-style: none; padding: 2px 0;
}
.agent-fallback > summary::-webkit-details-marker { display: none; }
.agent-fallback > summary::before { content: '▸ '; }
.agent-fallback[open] > summary::before { content: '▾ '; }
.agent-fallback > summary:hover { color: var(--text); }
.agent-fallback[open] > summary { margin-bottom: 6px; }
/* Inside the fallback the key box is a plain block, not a numbered step. */
.agent-fallback .agent-step { margin: 10px 0; }
.agent-desktop-actions { margin: 2px 0; }
.agent-download { text-decoration: none; display: inline-flex; align-items: center; }
.agent-desktop-note { margin-top: 8px; margin-bottom: 4px; }

.agent-cando { margin: 4px 0 8px; padding-left: 18px; font-size: 12.5px; line-height: 1.6; color: var(--text); }
.agent-cando li { margin-bottom: 2px; }
.agent-tip { font-style: italic; }
.agent-doc-title { font-weight: 600; color: var(--accent); font-style: normal; }

/* Inline help inside settings sections — click to expand */
.settings-help { margin-top: 8px; }
.settings-help summary {
  cursor: pointer; color: var(--accent, #7a3b2e); font-size: 12px;
  padding: 2px 0; user-select: none;
}
.settings-help summary:hover { text-decoration: underline; }
.settings-help ol {
  margin: 6px 0 0 18px; padding: 8px 12px 4px 12px;
  background: rgba(122, 59, 46, 0.05); border-left: 2px solid var(--accent-mid, rgba(122, 59, 46, 0.3));
  border-radius: 0 4px 4px 0; font-size: 12px; line-height: 1.5;
}
.settings-help ol li { margin-bottom: 4px; }
.settings-help a { color: var(--accent, #7a3b2e); }

.user-menu { position: relative; margin-left: 12px; }
.user-menu.hidden { display: none; }
.user-menu-btn {
  background: transparent; border: 1px solid var(--border); border-radius: 4px;
  padding: 4px 10px; cursor: pointer; font-size: 13px; color: var(--text);
  display: inline-flex; align-items: center; gap: 6px;
}
.user-menu-btn:hover { background: var(--accent-light); border-color: var(--accent-mid); }
.user-menu-color-dot {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--user-color, #7a3b2e);
}
.user-menu-dropdown {
  position: absolute; right: 0; top: 100%; margin-top: 4px;
  background: white; border: 1px solid rgba(0,0,0,0.15); border-radius: 4px;
  box-shadow: 0 4px 14px rgba(0,0,0,0.15); min-width: 220px; z-index: 1000;
}
.user-menu-dropdown.hidden { display: none; }
.user-menu-account { padding: 10px 14px 8px; }
.user-menu-account-email { font-size: 13px; font-weight: 600; color: #333; word-break: break-all; }
.user-menu-account-role { font-size: 11px; color: #888; margin-top: 2px; text-transform: uppercase; letter-spacing: 0.04em; }
.user-menu-dropdown .menu-item {
  display: block; width: 100%; text-align: left; background: transparent; border: none;
  padding: 8px 14px; cursor: pointer; font-size: 13px;
}
.user-menu-dropdown .menu-item:hover { background: rgba(122, 59, 46, 0.08); }
/* "Upgrade to Pro" CTA — reads as the primary action in the account menu; shown
   only to Free non-admin users when billing is configured (see updateUpgradeVisibility). */
.user-menu-dropdown .menu-item-upgrade { color: var(--accent); font-weight: 600; }
.user-menu-dropdown .menu-item-upgrade:hover { background: var(--accent); color: #fff; }

.signin-container {
  max-width: 480px; margin: 72px auto; padding: 34px 36px; text-align: left; font-size: 16px;
  background: white; border: 1px solid var(--border-strong, #ddd); border-radius: 8px;
}
.signin-container h2 { margin-top: 0; font-size: 30px; line-height: 1.2; }
.signin-hint { font-size: 15px; line-height: 22px; color: var(--text-dim, #888); }
#signin-form { display: flex; gap: 8px; margin-top: 12px; }
#signin-form input { flex: 1; padding: 11px 13px; font-size: 16px; border: 1px solid var(--border-strong, #ddd); border-radius: 4px; }
#signin-form button { padding: 11px 16px; font-size: 16px; background: var(--accent, #7a3b2e); color: white; border: none; border-radius: 4px; cursor: pointer; }
.signin-result { margin-top: 14px; font-size: 15px; }

/* Firebase sign-in (Slice 2) — shown only when /api/config carries a firebase config. */
.fb-provider-btn {
  display: block; width: 100%; margin-top: 14px; padding: 13px 16px;
  background: #fff; color: #222; border: 1px solid var(--border-strong, #ddd);
  border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: 600;
}
.fb-provider-btn:hover { background: var(--bg-soft, #f7f5f0); }
/* The action-handler page's way onward is a LINK, not a button, so it needs the
   three properties an anchor does not get for free. */
a.fb-provider-btn { text-align: center; text-decoration: none; box-sizing: border-box; color: #222; }
.fb-divider { display: flex; align-items: center; gap: 10px; margin: 18px 0; color: var(--text-dim, #999); font-size: 14px; }
.fb-divider::before, .fb-divider::after { content: ""; flex: 1; height: 1px; background: var(--border-strong, #e2dccd); }
.fb-email-form { display: flex; flex-direction: column; gap: 9px; }
.fb-email-form input { padding: 11px 13px; font-size: 16px; border: 1px solid var(--border-strong, #ddd); border-radius: 4px; }
.fb-email-form button { padding: 12px 16px; font-size: 16px; background: var(--accent, #7a3b2e); color: #fff; border: none; border-radius: 4px; cursor: pointer; }
.fb-link-btn { margin-top: 12px; background: none; border: none; color: var(--accent, #7a3b2e); cursor: pointer; font-size: 15px; padding: 0; text-decoration: underline; }
/* Signup consent: a conspicuous notice under the Google button (sign-in-wrap) and a
   required checkbox above the email "Create an account" action (clickwrap). */
/* The sign-in panel is one form in two modes. Name and date of birth belong to
   creating an account, so they are hidden while signing in rather than asked of
   everyone who wants to log in. */
.signin-container:not([data-mode="signup"]) .fb-signup-only { display: none; }
/* The third mode asks only for an address, so everything that belongs to having a
   password — the field, its reveal, the providers offering an alternative — goes
   away, and what is left is one input and one button. */
.signin-container:not([data-mode="reset"]) .fb-reset-only { display: none; }
.signin-container[data-mode="reset"] .fb-not-reset { display: none; }
.signin-container:not([data-mode="signin"]) .fb-signin-only { display: none; }
.fb-reset-note { font-size: 15px; line-height: 22px; color: var(--text-dim, #888); }
.fb-link-row { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; flex-wrap: wrap; }
/* The reveal sits inside the field rather than beside it, so the form stays one
   column of equal-width controls at every width. */
.fb-password-wrap { position: relative; display: flex; }
.fb-email-form .fb-password-wrap input { flex: 1; min-width: 0; padding-right: 62px; }
.fb-email-form .fb-reveal-btn {
  position: absolute; top: 1px; right: 1px; bottom: 1px;
  padding: 0 12px; background: none; color: var(--text-dim, #888);
  border: none; border-radius: 0 4px 4px 0; font-size: 13.5px; cursor: pointer;
}
.fb-email-form .fb-reveal-btn:hover { color: var(--accent, #7a3b2e); }
.fb-email-form .fb-reveal-btn:focus-visible { outline: 2px solid var(--accent, #7a3b2e); outline-offset: -2px; }
.fb-name-row { display: flex; gap: 8px; }
.fb-name-row input { flex: 1; min-width: 0; }
.fb-field-label { display: block; font-size: 12px; color: var(--text-dim, #888); margin: 4px 0 2px; }
.fb-field-note { margin: 4px 0 2px; font-size: 12.5px; line-height: 18px; color: var(--text-dim, #999); }
/* A date input is shorter than a text input by default in Safari; match the rest. */
.fb-email-form input[type="date"] { width: 100%; box-sizing: border-box; }
/* Account modal: one concern per section, with deletion visually set apart so it is
   never reached by accident on the way to something else. */
.account-section { margin-bottom: 20px; }
.account-section > label { display: block; font-size: 12px; color: var(--text-dim, #888); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.5px; }
.account-identity { font-size: 15px; }
.account-plan-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.account-plan-name { font-size: 15px; font-weight: 600; color: var(--accent, #7a3b2e); }
.account-plan-detail { font-size: 13px; color: var(--text-dim, #888); }
.account-plan-actions { margin-top: 8px; display: flex; gap: 8px; flex-wrap: wrap; }
.account-plan-actions button { padding: 6px 14px; border-radius: 4px; cursor: pointer; border: 1px solid var(--border-strong, #ddd); background: var(--surface, #fff); }
.account-danger { border-top: 1px solid rgba(0,0,0,0.08); padding-top: 16px; }
.account-danger-btn {
  margin-top: 8px; padding: 6px 14px; border-radius: 4px; cursor: pointer;
  background: transparent; color: #a11; border: 1px solid #a11;
}
.account-danger-btn:hover { background: color-mix(in srgb, #a11 8%, transparent); }
/* One choice per shared paper on the delete screen: hand it to a coauthor (the default,
   listed first) or delete it. */
.account-delete-choices { margin: 2px 0 8px; }
.account-delete-choice {
  margin: 0 0 8px; padding: 6px 10px 8px;
  border: 1px solid rgba(0,0,0,0.1); border-radius: 6px;
}
.account-delete-choice legend { padding: 0 4px; font-size: 13px; font-weight: 600; color: var(--text, #333); }
.account-delete-choice label {
  display: inline-flex; align-items: center; gap: 5px; margin-right: 18px;
  font-size: 13px; cursor: pointer;
}
.fb-legal-note { margin: 9px 0 0; font-size: 13.5px; line-height: 20px; color: var(--text-dim, #999); }
.fb-legal-note a, .fb-consent a { color: var(--accent, #7a3b2e); }
.fb-consent { display: flex; align-items: flex-start; gap: 9px; margin: 18px 0 0; font-size: 14.5px; line-height: 21px; color: var(--text, #333); cursor: pointer; }
.fb-consent input { width: 16px; height: 16px; margin: 2px 0 0; flex-shrink: 0; cursor: pointer; }
.fb-consent.err { color: var(--accent, #7a3b2e); font-weight: 600; }
/* Purchase-intent note shown atop the sign-in panel when arriving via "Get Pro". */
.fb-purchase-note { margin: 0 0 18px; font-size: 16px; line-height: 24px; color: color-mix(in srgb, var(--text, #333) 78%, transparent); }
.fb-purchase-note strong { color: var(--accent, #7a3b2e); }

.share-btn {
  margin-left: 8px; padding: 4px 12px; font-size: 12px; cursor: pointer;
  background: transparent; color: var(--accent, #7a3b2e);
  border: 1px solid transparent; border-radius: 4px;
}
.share-btn:hover { background: var(--accent-light); }

/* Modal scaffolding (settings, share) */
.settings-section { margin-bottom: 14px; }
.settings-section label { display: block; font-size: 12px; color: var(--text-dim, #888); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.5px; }
.settings-section input[type=text],
.settings-section input[type=password],
.settings-section input[type=color] {
  width: 100%; padding: 6px 10px; border: 1px solid var(--border-strong, #ddd); border-radius: 4px; font-family: inherit; font-size: 13px;
}
.settings-section input[type=color] { padding: 2px; height: 36px; cursor: pointer; }
.settings-row { display: flex; gap: 8px; align-items: center; }
.settings-row input { flex: 1; }
.settings-state { font-size: 11px; color: var(--text-dim, #888); }
.settings-actions { display: flex; gap: 12px; align-items: center; margin-top: 16px; }
.settings-actions button { padding: 6px 16px; background: var(--accent, #7a3b2e); color: white; border: none; border-radius: 4px; cursor: pointer; }
.settings-saved-msg { color: #2a7a2a; font-size: 12px; }

/* Settings service list + per-service detail popup */
.service-list { list-style: none; padding: 0; margin: 0; }
.service-list li { margin: 0 0 6px; }
.service-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; text-align: left; padding: 10px 12px;
  background: var(--surface, #fff); border: 1px solid var(--border, #e0ddd6);
  border-radius: 6px; cursor: pointer; font-family: inherit; font-size: 14px; color: var(--text);
}
.service-row:hover { border-color: var(--accent-mid, #c47a68); background: var(--accent-light, #f2e4e0); }
.service-row-name { font-weight: 500; }
.service-row-state { font-size: 11px; color: var(--text-dim, #888); white-space: nowrap; }
.service-row-state.is-ok { color: #2a7a2a; }
.service-instructions { margin-top: 12px; }
.service-instructions ol { margin: 0; padding-left: 20px; font-size: 13px; color: var(--text-dim, #555); line-height: 1.5; }
.service-instructions ol li { margin-bottom: 5px; }
.service-instructions a { color: var(--accent, #7a3b2e); }

/* Share modal */
.share-section { margin-bottom: 16px; }
.share-section label { display: block; font-size: 12px; color: var(--text-dim, #888); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.5px; }
.share-owner-line { font-size: 13px; color: var(--text-dim, #888); margin-bottom: 8px; }
.share-list { list-style: none; padding: 0; margin: 0; }
.share-list li { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid rgba(0,0,0,0.06); }
.share-email { color: var(--text-dim, #888); font-size: 12px; flex: 1; }
.share-remove { background: transparent; border: 1px solid var(--border-strong, #ddd); border-radius: 3px; padding: 2px 8px; font-size: 11px; cursor: pointer; }
.share-remove:hover { background: rgba(122, 59, 46, 0.08); border-color: var(--accent, #7a3b2e); color: var(--accent, #7a3b2e); }
/* Role picker: one per collaborator row, and one on the invite line. Sits before
   Remove so the row reads "who — what they may do — take it away". */
.share-role-select {
  border: 1px solid var(--border-strong, #ddd); border-radius: 3px;
  padding: 2px 4px; font-size: 11px; background: transparent;
  color: inherit; cursor: pointer;
}
.share-role-select:disabled { opacity: 0.5; cursor: progress; }
/* The same information for someone who may not change it. */
.share-role-tag {
  font-size: 11px; text-transform: capitalize; opacity: 0.7;
  border: 1px solid var(--border-strong, #ddd); border-radius: 3px; padding: 2px 6px;
}
.share-empty { color: var(--text-dim, #888); font-size: 13px; }
.share-invite-row { display: flex; gap: 8px; }
.share-invite-row input { flex: 1; padding: 6px 10px; border: 1px solid var(--border-strong, #ddd); border-radius: 4px; }
.share-invite-row button { padding: 6px 14px; background: var(--accent, #7a3b2e); color: white; border: none; border-radius: 4px; cursor: pointer; }
.share-invite-msg { font-size: 12px; margin-top: 6px; color: var(--text-dim, #888); }
/* The link row reuses .share-invite-row's geometry but reads as the secondary
   action: a readonly field and an outline button, so Invite keeps its weight.
   min-width holds the button steady while its label swaps to "Copied". */
#share-link { background: var(--surface2, #faf8f5); color: var(--text-dim, #666); }
#share-copy-link {
  background: transparent; color: var(--accent, #7a3b2e);
  border: 1px solid var(--accent, #7a3b2e); min-width: 92px;
}
#share-copy-link:hover { background: color-mix(in srgb, var(--accent, #7a3b2e) 8%, transparent); }
/* A bounce diagnostic is written by the receiving mail server, so its length is not
   ours to predict: Resend's generic hard-bounce text runs past 250 characters and
   widened the table until the row's "Allow again" button sat off-screen behind a
   horizontal scrollbar. One ellipsed line keeps the action reachable; the full text
   is on the cell's title. */
.suppression-detail {
  max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Slice 4: pending invites (invited but not yet signed in via Firebase). */
.share-pending .share-email { opacity: 0.8; }
.share-pending-tag { font-size: 11px; font-style: italic; color: var(--text-dim, #888); }

/* Chat tabs (Claude / Team) */
/* Horizontal scroll when the pane is too narrow to show every tab.
   Native scrollbars are unreliable here — macOS Chromium uses overlay
   bars that disappear at rest and ignore most ::-webkit-scrollbar
   styling. We hide them entirely and provide explicit chevron buttons
   (rendered by JS when scrollWidth > clientWidth). min-width: 0 on the
   parent pane (below) lets the chat pane actually shrink to its
   flex-allocated width instead of expanding to fit the tabs. */
.chat-tabs-wrap {
  position: relative; display: flex; align-items: stretch;
  padding: 6px 10px 0; min-width: 0;
  background: var(--bg);
}
/* The tabs WRAP rather than scroll. The seven of them total ~607px against a
   ~348px row, so scrolling left three of them off the edge at any moment — and a
   tab you cannot see is a feature you never learn exists, which is the whole
   reason the plan-gated ones are shown locked rather than hidden at all. Wrapping
   costs one row of vertical space in a pane that has the height to spare, and it
   scales: the More-AIs provider tabs are added at runtime and would otherwise
   push the row further past its edge. The ‹ › chevrons below are kept and simply
   stop appearing (their own test is scrollWidth > clientWidth, which wrapping
   makes false); they still cover the degenerate case of one tab wider than the
   whole row. */
.chat-tabs {
  display: flex; gap: 2px; flex: 1 1 auto;
  flex-wrap: wrap; row-gap: 3px; overflow-y: hidden;
  scrollbar-width: none;
}
.chat-tabs::-webkit-scrollbar { display: none; }
.chat-tabs-chevron {
  flex: 0 0 auto; align-self: center;
  background: transparent; border: 1px solid transparent;
  border-radius: 4px; color: var(--accent, #7a3b2e);
  cursor: pointer; padding: 2px 6px; margin: 0 2px;
  font-size: 14px; line-height: 1; user-select: none;
}
.chat-tabs-chevron:hover { background: var(--accent-light); }
.chat-tabs-chevron[disabled] { opacity: 0.35; cursor: default; }
.chat-tab {
  padding: 4px 12px; font-size: 12px; cursor: pointer; flex: 0 0 auto;
  white-space: nowrap;
  background: transparent; border: 1px solid transparent;
  border-bottom: none; border-radius: 4px 4px 0 0; color: var(--text-dim, #888);
}
.chat-tab:hover { background: transparent; color: var(--accent); border-color: transparent; }
.chat-tab.active {
  background: transparent; color: var(--accent);
  border-color: transparent; border-bottom: 2px solid var(--accent);
}
.chat-tab-dynamic { display: inline-flex; align-items: center; gap: 6px; }
.chat-tab-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; border-radius: 3px; font-size: 13px; line-height: 1;
  color: var(--text-dim, #888);
}
.chat-tab-close:hover { background: rgba(122, 59, 46, 0.12); color: var(--accent, #7a3b2e); }

/* "More AIs" launcher */
.more-chooser { padding: 18px; }
.more-chooser-hint { font-size: 13px; color: var(--text-dim, #888); margin: 0 0 12px; }
.more-chooser-btns { display: flex; flex-direction: column; gap: 8px; }
.more-chooser-btn {
  text-align: left; padding: 10px 12px; font-size: 14px; cursor: pointer;
  background: var(--surface, #fff); border: 1px solid var(--border, #e0ddd6);
  border-radius: 6px; color: var(--text);
}
.more-chooser-btn:hover { border-color: var(--accent-mid, #c47a68); background: var(--accent-light, #f2e4e0); }
/* Comments side panel */
.comments-empty { padding: 16px; color: var(--text-dim, #888); font-size: 13px; text-align: center; }
.comment-card {
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 4px;
  padding: 10px 12px;
  margin: 8px 10px;
  background: #fff;
}
.comment-card-jumpable { cursor: pointer; transition: background 120ms; }
.comment-card-jumpable:hover { background: rgba(122, 59, 46, 0.04); }

/* "More AIs" tab — BoodleBox iframe panel. The bar at the top carries the
   open-in-new-tab fallback for visitors whose browser (or BoodleBox's
   X-Frame-Options) blocks the embed. */
.more-iframe-wrap { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.more-iframe-wrap.hidden { display: none; }
.more-iframe-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 10px; border-bottom: 1px solid var(--border);
  font-size: 12px; color: var(--text-dim); background: var(--surface2);
}
.more-iframe-bar a { color: var(--accent, #7a3b2e); text-decoration: none; }
.more-iframe-bar a:hover { text-decoration: underline; }
#more-iframe { flex: 1; width: 100%; border: 0; min-height: 0; }

/* Version history modal. Each row carries the timestamp + author + a
   small badge for the trigger (auto / compile / export / manual). */
.version-history-card { min-width: 560px; max-width: 800px; width: 80vw; max-height: 80vh; }
.version-history-actions { display: flex; align-items: center; gap: 8px; }
.version-list { list-style: none; padding: 0; margin: 0; max-height: 60vh; overflow-y: auto; }
.version-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-bottom: 1px solid var(--border);
}
.version-row:hover { background: rgba(0,0,0,0.02); }
.version-meta { flex: 1; min-width: 0; }
.version-meta-line { font-size: 13px; color: var(--text); }
.version-meta-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
.version-badge {
  display: inline-block; font-size: 10px; font-weight: 600; text-transform: uppercase;
  padding: 1px 6px; border-radius: 3px; margin-right: 6px; letter-spacing: 0.4px;
}
.version-badge-manual   { background: rgba(122, 59, 46, 0.15); color: #7a3b2e; }
.version-badge-compile  { background: rgba(46, 92, 122, 0.15); color: #2e5c7a; }
.version-badge-export   { background: rgba(46, 122, 70, 0.15); color: #2e7a46; }
.version-badge-auto     { background: rgba(0,0,0,0.06);        color: var(--text-dim); }
.version-actions { display: flex; gap: 6px; }
.version-actions button { font-size: 12px; padding: 4px 10px; cursor: pointer; }
.version-empty { padding: 24px 12px; color: var(--text-dim); text-align: center; font-size: 13px; }
.version-preview-card { min-width: 640px; max-width: 1000px; width: 90vw; max-height: 86vh; }
.version-preview-actions { display: flex; align-items: center; gap: 8px; }
.version-preview-body {
  background: var(--code-bg, #fafafa); border: 1px solid var(--border);
  border-radius: 4px; padding: 12px; font-family: 'Menlo', monospace;
  font-size: 12px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
  max-height: 60vh; overflow: auto; margin: 0;
}
.comment-head { font-size: 11px; margin-bottom: 4px; }
.comment-author { font-weight: 600; }
.comment-time { color: var(--text-dim, #888); margin-left: 6px; }
.comment-snippet {
  font-size: 12px;
  font-style: italic;
  color: var(--text-dim, #555);
  background: rgba(0, 0, 0, 0.03);
  border-left: 3px solid rgba(122, 59, 46, 0.35);
  padding: 4px 8px;
  margin: 4px 0 6px;
  cursor: pointer;
  border-radius: 0 3px 3px 0;
}
.comment-snippet:hover { background: rgba(122, 59, 46, 0.08); }
.comment-snippet-lost { color: #a33; font-style: normal; cursor: default; border-left-color: #a33; }
.comment-snippet-lost:hover { background: rgba(0, 0, 0, 0.03); }
.comment-body { font-size: 13px; white-space: pre-wrap; }
.comment-actions { display: flex; gap: 6px; margin-top: 6px; }
.comment-action {
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  padding: 2px 8px;
  cursor: pointer;
  font-size: 11px;
  color: var(--text, #2a2a2a);
}
.comment-action:hover { background: rgba(122, 59, 46, 0.10); border-color: rgba(122, 59, 46, 0.4); }
.comment-action-danger:hover { background: rgba(170, 51, 51, 0.10); border-color: rgba(170, 51, 51, 0.55); color: #a33; }
.comment-replies { margin-top: 8px; padding-left: 12px; border-left: 2px solid rgba(0, 0, 0, 0.06); display: flex; flex-direction: column; gap: 6px; }
.comment-reply { background: rgba(0, 0, 0, 0.02); border-radius: 3px; padding: 6px 8px; }
.comment-reply-form { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.comment-reply-form textarea { width: 100%; min-height: 50px; padding: 6px 8px; box-sizing: border-box; resize: vertical; font: inherit; }
.comment-reply-form button { align-self: flex-end; padding: 4px 12px; background: var(--accent, #7a3b2e); color: #fff; border: none; border-radius: 3px; cursor: pointer; font-size: 12px; }
.comment-reply-form button:hover { background: #923f30; }

.team-msg { padding: 6px 10px; border-bottom: 1px solid rgba(0,0,0,0.05); position: relative; }
.team-msg-head { font-size: 11px; }
.team-msg-name { font-weight: 600; }
.team-msg-time { color: var(--text-dim, #888); margin-left: 6px; }
.team-msg-body { font-size: 13px; white-space: pre-wrap; margin-top: 2px; }
/* Long messages (a shared AI transcript, typically) collapse behind a toggle. */
.team-msg-body.clamped {
  max-height: 9em; overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}
.team-msg-more {
  margin-top: 4px; padding: 1px 6px; font-size: 11px;
  color: var(--text-dim); background: none;
  border: 1px solid var(--border); border-radius: 3px;
}
.team-msg-more:hover { color: var(--accent); border-color: var(--accent-mid); }
.team-msg-actions { display: none; gap: 6px; margin-top: 4px; }
.team-msg:hover .team-msg-actions { display: flex; }
.team-msg-edit, .team-msg-delete {
  background: rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 3px;
  padding: 2px 8px;
  cursor: pointer;
  font-size: 11px;
  color: var(--text, #2a2a2a);
}
.team-msg-edit:hover, .team-msg-delete:hover {
  background: rgba(122, 59, 46, 0.10);
  border-color: rgba(122, 59, 46, 0.4);
}

/* Editor-side tabs (Source | PDF) */
.editor-tabs {
  display: flex; gap: 2px; padding: 6px 10px 0; flex-shrink: 0;
  background: var(--bg); border-bottom: 1px solid var(--border);
}
/* A tab being dragged to reorder (editor or chat tabs). */
.tab-dragging { opacity: 0.45; cursor: grabbing; }
.editor-tab {
  padding: 4px 14px; font-size: 12px; cursor: pointer;
  background: transparent; border: 1px solid transparent;
  border-bottom: none; border-radius: 4px 4px 0 0; color: var(--text-dim, #888);
}
.editor-tab:hover { background: transparent; color: var(--accent); border-color: transparent; }
.editor-tab.active {
  background: transparent; color: var(--accent);
  border-color: transparent; border-bottom: 2px solid var(--accent);
}
.editor-tab.hidden { display: none; }
/* Metered daily LaTeX-render count on the "LaTeX PDF" tab (Free tier). A small
   pill showing renders left today; turns red at zero. Hidden for Pro/admin. */
.tab-quota {
  display: inline-block; margin-left: 6px; padding: 0 5px; min-width: 15px;
  font-size: 10px; font-weight: 600; line-height: 15px; text-align: center;
  border-radius: 8px; color: #fff; background: var(--accent, #4a7);
  vertical-align: middle;
}
.tab-quota.is-empty { background: var(--danger, #d24); }
.tab-quota.hidden { display: none; }

/* Free-tier page-cap banner (R7): a persistent notice when the user's OWN document
   exceeds the plan's page limit. Only the LaTeX-PDF render is gated; editing and the
   always-free "PDF" tab are not. */
.page-cap-banner {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 16px;
  background: #fff4e5; border-bottom: 1px solid #f0c987; color: #7a4f10;
  font-size: 13px; line-height: 1.4;
}
.page-cap-banner.hidden { display: none; }
.page-cap-banner #page-cap-text { flex: 1; }
.page-cap-upgrade {
  flex: none; padding: 5px 12px; border: none; border-radius: 6px;
  background: var(--accent-strong, #a03232); color: #fff;
  font-weight: 600; font-size: 12px; cursor: pointer;
}
.page-cap-upgrade:hover { background: #8a2a2a; }
@media (prefers-color-scheme: dark) {
  .page-cap-banner { background: #3a2c14; border-bottom-color: #5c4620; color: #f0d9a8; }
}

/* Citations with no reference behind them, raised after a compile or an export.
   Amber rather than red: the document compiled and the file downloaded, so this
   is something to fix before submitting, not a failure. Dismissible, because the
   writer may already know and be mid-way through linking them. */
.cite-warning-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px;
  background: #fff4e5; border-bottom: 1px solid #f0c987; color: #7a4f10;
  font-size: 13px; line-height: 1.4;
}
.cite-warning-banner.hidden { display: none; }
.cite-warning-badge {
  flex: none; padding: 2px 8px; border-radius: 10px;
  background: #a8710f; color: #fff;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
}
.cite-warning-banner #cite-warning-text { flex: 1; }
.cite-warning-action {
  flex: none; padding: 4px 10px; border: 1px solid #d9a441; border-radius: 5px;
  background: transparent; color: #7a4f10; font-size: 12px; font-weight: 600; cursor: pointer;
}
.cite-warning-action:hover { background: rgba(168, 113, 15, 0.12); }
.cite-warning-action.hidden { display: none; }
.cite-warning-close {
  flex: none; border: none; background: transparent; color: inherit;
  font-size: 17px; line-height: 1; cursor: pointer; opacity: 0.6; padding: 0 2px;
}
.cite-warning-close:hover { opacity: 1; }
@media (prefers-color-scheme: dark) {
  .cite-warning-banner { background: #3a2c14; border-bottom-color: #5c4620; color: #f0d9a8; }
  .cite-warning-badge { background: #c08a1e; color: #1c1509; }
  .cite-warning-action { border-color: #7a5c1e; color: #f0d9a8; }
}

/* The document on screen came from this browser's cache and was never confirmed
   against the server, and that cache is from an earlier day. RED, not the amber the
   citation and Word bars use: those two both mean "nothing is broken, but somebody
   has to act", and a third amber bar would be read as one more of those. This one
   means the text you are reading is not the paper. It is also FIRST in the stack,
   because it invalidates the notices under it. Not dismissible — the notice IS the
   condition, and it clears itself the moment a sync completes. */
.stale-copy-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px;
  background: #fdecea; border-bottom: 1px solid #f0b3ac; color: #8a2318;
  font-size: 13px; line-height: 1.4;
}
.stale-copy-banner.hidden { display: none; }
.stale-copy-badge {
  flex: none; padding: 2px 8px; border-radius: 10px;
  background: #b3261e; color: #fff;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
}
.stale-copy-banner #stale-copy-text { flex: 1; }
.stale-copy-banner strong { font-weight: 600; }
.stale-copy-action {
  flex: none; padding: 4px 10px; border: 1px solid #d98a80; border-radius: 5px;
  background: transparent; color: #8a2318; font-size: 12px; font-weight: 600; cursor: pointer;
}
.stale-copy-action:hover { background: rgba(179, 38, 30, 0.12); }
@media (prefers-color-scheme: dark) {
  .stale-copy-banner { background: #3d1f1c; border-bottom-color: #6b332c; color: #f5c9c2; }
  .stale-copy-badge { background: #d1443a; color: #1a0d0b; }
  .stale-copy-action { border-color: #7d3a32; color: #f5c9c2; }
}

/* Word edits waiting on review. Amber like the citation warning, because it is the
   same kind of thing: nothing is broken, but somebody has to act. The badge takes
   Word's own blue so the two amber banners are told apart at a glance by the badge
   they were given badges for. Not dismissible — the notice IS the condition, and it
   goes away by reviewing (which is one click away in the bar itself). */
.word-sync-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px;
  background: #fff4e5; border-bottom: 1px solid #f0c987; color: #7a4f10;
  font-size: 13px; line-height: 1.4;
}
.word-sync-banner.hidden { display: none; }
.word-sync-badge {
  flex: none; padding: 2px 8px; border-radius: 10px;
  background: #2b579a; color: #fff;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
}
.word-sync-banner #word-sync-text { flex: 1; }
.word-sync-action {
  flex: none; padding: 4px 10px; border: 1px solid #d9a441; border-radius: 5px;
  background: transparent; color: #7a4f10; font-size: 12px; font-weight: 600; cursor: pointer;
}
.word-sync-action:hover { background: rgba(168, 113, 15, 0.12); }
@media (prefers-color-scheme: dark) {
  .word-sync-banner { background: #3a2c14; border-bottom-color: #5c4620; color: #f0d9a8; }
  .word-sync-badge { background: #4a7ac0; color: #0b1220; }
  .word-sync-action { border-color: #7a5c1e; color: #f0d9a8; }
}

/* ---------- Read-only collaborator (viewer / commenter) ----------
   The server is the authority: a viewer's Y.Doc socket drops writes and their
   HTTP writes are refused. Everything here exists so the INTERFACE tells the
   same story, because a control that looks live and then does nothing is worse
   than one that is plainly unavailable.

   Note what this is NOT: a security boundary. A control missed here costs a
   confusing click, never an edit — which is why hiding whole menus and marking
   the mixed ones is enough, and an exhaustive list of element ids (which would
   fall behind the next button somebody adds) is not needed. */
.doc-role-banner {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px;
  background: #eef2f8; border-bottom: 1px solid #c3d0e3; color: #33475f;
  font-size: 13px; line-height: 1.4;
}
.doc-role-banner.hidden { display: none; }
.doc-role-badge {
  flex: none; padding: 2px 8px; border-radius: 10px;
  background: #33475f; color: #fff;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase;
}
@media (prefers-color-scheme: dark) {
  .doc-role-banner { background: #1e2833; border-bottom-color: #35485c; color: #c6d6e8; }
  .doc-role-badge { background: #4a6a8f; }
}

/* Menus whose every item changes the document. Hidden rather than disabled: a
   greyed-out Format menu invites a click to find out why. */
body.doc-read-only #format-menu-btn,
body.doc-read-only #insert-menu-btn,
body.doc-read-only .toolbar,
body.doc-read-only #editor-ruler {
  display: none !important;
}

/* Mixed menus and standalone controls: opt an item OUT by marking it in the
   markup, so the reason travels with the element. */
body.doc-read-only [data-needs-edit] { display: none !important; }

/* A commenter keeps the one thing that distinguishes them from a viewer. */
body.doc-commenter [data-needs-comment] { display: revert !important; }

/* The editing/suggesting control becomes a plain label of what you are. */
body.doc-read-only #suggest-mode-btn { opacity: 0.75; cursor: default; }
body.doc-read-only #suggest-mode-menu { display: none !important; }

/* A reader cannot accept or reject anything, so the Word notice would be a standing amber
   bar about somebody else's workflow with a button that does nothing — the exact "looks
   live and then does nothing" this section exists to prevent. Hidden in the stylesheet,
   off the same body class as every other write control, rather than in a JS branch that
   the next banner would have to remember to repeat. */
body.doc-read-only .word-sync-banner { display: none !important; }

/* No text caret in a document you cannot type into. Set on the pane root, not
   the ProseMirror element, so it reaches the page-box clones too. */
.mirror-read-only .ProseMirror { caret-color: transparent; }

/* The page ruler stays — it tells a reader where the margins are, which is worth
   knowing — but its drag handles go: they rewrite \geometry and \parindent in the
   preamble, and a handle that can be grabbed and does nothing is worse than none. */
.mirror-read-only .pg-ruler-handle { display: none !important; }
.mirror-read-only .pg-ruler { cursor: default; }

/* Appendix family row: document-selector chips + create button, separated
   from the Source/Clean/PDF view-mode tabs by a thin divider. */
.appendix-tabs {
  display: flex; gap: 2px; align-items: flex-end;
  margin-left: 8px; padding-left: 8px;
  border-left: 1px solid var(--border-strong, #ddd);
}
.appendix-tabs:empty { margin-left: 0; padding-left: 0; border-left: none; }
.appendix-tab { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.add-appendix-btn {
  margin-left: 6px; color: var(--accent, #7a3b2e);
  font-size: 15px; line-height: 1; font-weight: 600;
}
.add-appendix-btn:hover { background: var(--accent-light); }
.add-appendix-btn:disabled { opacity: 0.5; cursor: default; }
/* The AI-suggestions appendix chip reads distinctly from ordinary appendices. */
.appendix-tab-ai { color: #7b5cd6; }
.appendix-tab-ai.active { color: #7b5cd6; border-color: transparent; border-bottom-color: #7b5cd6; }
/* Close (✕) affordance on the currently-open appendix chip. */
.appendix-tab-close {
  margin-left: 7px; font-weight: 700; line-height: 1; opacity: 0.55;
  padding: 0 3px; border-radius: 3px; cursor: pointer;
}
.appendix-tab-close:hover { opacity: 1; color: #c0392b; background: rgba(0, 0, 0, 0.08); }
/* Danger action in the appendix-close dialog. */
.tb-btn.appendix-close-danger { color: #c0392b; }
.tb-btn.appendix-close-danger:hover { background: #fbe9e9; border-color: #c0392b; color: #a3271b; }

/* Clean-view AI-suggestion reference marker: a distinct "✦ N" pill the reader
   clicks to open the linked entry in the AI Notes appendix. Deliberately styled
   as an AI-note chip (the same ✦ as the AI Notes tab and chat breadcrumb), not
   a bare superscript, so it stands out from citations and is not overlooked.
   The number is the literal source digit (the \aisuggestionref{ } wrapper is
   hidden), so a pending tracked-change still underlines it. */
.cm-editor-aisug-ref {
  font-size: 0.72em; font-weight: 700;
  color: #5a3fb0;
  background: rgba(123, 92, 214, 0.16);
  border: 1px solid rgba(123, 92, 214, 0.45);
  border-radius: 8px;
  padding: 0 5px;
  margin: 0 2px;
  vertical-align: 0.22em;
  cursor: pointer;
  white-space: nowrap;
}
.cm-editor-aisug-ref::before { content: "\2726\00a0"; } /* "✦ " */
.cm-editor-aisug-ref:hover { background: rgba(123, 92, 214, 0.32); }

/* When the source pane is "hidden" via tab switching, hide it but keep CodeMirror's
   layout intact; toggling display back doesn't blow away decorations. */
#editor.hidden { display: none; }

/* PDF tab */
.pdf-pane { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.pdf-pane.hidden { display: none; }

/* Split view: editor on the left, PDF on the right, within the editor pane.
   The tabs span the top; the editor-ruler (row 2, collapses to 0 when hidden in
   source mode) and #editor stack in the left column; #pdf-pane fills the right
   column across both content rows. Badges stay position:absolute over the pane. */
.editor-pane.split-view {
  display: grid;
  /* fr-ratio columns so the split survives pane resizes; the divider (col 2) is
     dragged to rewrite the two ratios. Defaults to 50/50. */
  grid-template-columns: var(--pdf-split-left, 1fr) 6px var(--pdf-split-right, 1fr);
  grid-template-rows: auto auto 1fr;
}
.editor-pane.split-view .editor-tabs { grid-column: 1 / 4; grid-row: 1; }
.editor-pane.split-view #editor-ruler  { grid-column: 1; grid-row: 2; }
.editor-pane.split-view #editor       { grid-column: 1; grid-row: 3; min-width: 0; }
.editor-pane.split-view #pdf-split-divider { grid-column: 2; grid-row: 2 / 4; }
.editor-pane.split-view #pdf-pane     { grid-column: 3; grid-row: 2 / 4; min-width: 0; }
/* The divider only exists in split view; a thin draggable bar (Overleaf-style). */
.pdf-split-divider { display: none; }
.editor-pane.split-view .pdf-split-divider {
  display: block; cursor: col-resize; background: var(--border, #ddd);
  position: relative; z-index: 5; transition: background 0.12s;
}
.editor-pane.split-view .pdf-split-divider::after {
  content: ""; position: absolute; inset: 0 -4px; /* widen the grab target */
}
.editor-pane.split-view .pdf-split-divider:hover,
.editor-pane.split-view .pdf-split-divider.dragging { background: var(--accent, #8a3324); }
/* The split toggle reads as engaged when active. */
#pdf-split-btn.is-active { background: var(--accent-dark, #5a2a1e); box-shadow: inset 0 0 0 2px rgba(255,255,255,0.35); }
.pdf-toolbar {
  display: flex; gap: 8px; align-items: center; padding: 6px 10px;
  background: var(--surface2); border-bottom: 1px solid var(--border);
}
.pdf-toolbar button {
  padding: 4px 12px; font-size: 12px; cursor: pointer;
  background: var(--accent, #7a3b2e); color: white; border: none; border-radius: 3px;
}
.pdf-toolbar button:hover { background: var(--accent-dark, #5a2a1e); }
.pdf-toolbar #pdf-download-btn { background: transparent; color: var(--accent, #7a3b2e); border: 1px solid var(--accent-mid, rgba(122, 59, 46, 0.4)); }
.pdf-toolbar #pdf-download-btn.hidden { display: none; }
.pdf-status { font-size: 12px; color: var(--text-dim, #888); }
.pdf-status.ok { color: #2a7a2a; }
.pdf-status.warn { color: #b07a00; }
.pdf-status.error { color: #b00; }
.pdf-prewarn {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; background: #fff7e6; border: 1px solid #f5c97a;
  border-left: 3px solid #d97706; font-size: 12px; color: #6b3a04;
}
.pdf-prewarn.hidden { display: none; }
.pdf-prewarn-text { flex: 1; line-height: 1.45; }
.pdf-prewarn button {
  padding: 4px 10px; font-size: 12px; cursor: pointer;
  background: #d97706; color: white; border: 1px solid #b76304; border-radius: 3px;
}
.pdf-prewarn button:hover { background: #b76304; }
.pdf-frame { flex: 1; width: 100%; border: none; background: #555; }
.pdf-log {
  flex: 0 0 auto; max-height: 40%; margin: 0; padding: 8px 10px;
  background: #1f1f1f; color: #f0a0a0; font-size: 11px; overflow: auto;
  white-space: pre-wrap; word-break: break-word;
}
.pdf-log.hidden { display: none; }

/* Located compile errors, above the raw log. Deliberately in the app's own
   typeface and colours rather than the log's terminal styling: this is the part a
   writer is meant to read and act on, and the log below it is reference. */
.pdf-errors {
  flex: 0 0 auto; max-height: 40%; overflow: auto;
  padding: 10px 12px;
  background: var(--accent-light); border-top: 1px solid var(--accent-mid);
  font-size: 13px; line-height: 1.45;
}
.pdf-errors.hidden { display: none; }
.pdf-error-head { margin: 0 0 8px; font-weight: 600; color: var(--text); }
.pdf-error { padding: 7px 0; border-top: 1px solid var(--accent-mid); }
.pdf-error:first-of-type { border-top: none; }
.pdf-error-title { font-weight: 600; color: var(--text); margin-right: 8px; }
.pdf-error-jump {
  padding: 1px 9px; border: 1px solid var(--accent); border-radius: 999px;
  background: var(--surface); color: var(--accent);
  font-family: inherit; font-size: 12px; cursor: pointer; vertical-align: baseline;
}
.pdf-error-jump:hover { background: var(--accent); color: #fff; }
.pdf-error-noline { font-size: 12px; color: var(--text-dim); font-style: italic; }
.pdf-error-detail { margin-top: 3px; color: var(--text-dim); font-size: 12px; }
.pdf-error-excerpt {
  display: block; margin-top: 5px; padding: 4px 7px; border-radius: 3px;
  background: var(--code-bg); color: var(--text);
  font-family: 'Menlo', 'Courier New', monospace; font-size: 11.5px;
  white-space: pre-wrap; word-break: break-word;
}

/* The error lines themselves, marked in the Source tab. A tinted line plus a dot
   in its own gutter, so a compile failure is findable by scrolling and not only by
   clicking through from the panel. */
.cm-cw-error-line { background: rgba(148, 12, 36, 0.10); }
.cm-cw-error-gutter { min-width: 10px; }
.cm-cw-error-dot { color: var(--accent); font-size: 9px; line-height: 1; }
@media (prefers-color-scheme: dark) {
  .pdf-errors { background: #2c1218; border-top-color: #7a2437; }
  .pdf-error { border-top-color: #7a2437; }
  .pdf-error-jump { background: #1e1c1b; }
  .cm-cw-error-line { background: rgba(200, 60, 90, 0.16); }
}

/* Generic hidden helper: any element with class="hidden" goes away. Used by
   chat tab switching, settings modal sections, and the user menu dropdown.
   Existing element-specific .hidden rules above still match, but the
   important keyword guarantees this wins regardless of source order. */
.hidden { display: none !important; }

/* Team chat panel takes the full chat-pane height when active. */
#team-chat-messages { flex: 1; overflow-y: auto; padding: 6px 0; }
#team-chat-input-area textarea { width: 100%; padding: 6px 10px; box-sizing: border-box; min-height: 60px; resize: vertical; }
#team-chat-input-area button { margin-top: 6px; padding: 6px 14px; background: var(--accent, #7a3b2e); color: white; border: none; border-radius: 4px; cursor: pointer; }


/* "New version available" bar. Shown by showUpdateBanner() when a deploy is
   detected in an open tab; floats bottom-centre, above everything (ceiling was
   9999), and is dark so it reads over any page. */
.cw-update-banner {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  z-index: 10000;
  display: flex; align-items: center; gap: 14px;
  max-width: calc(100vw - 32px);
  padding: 10px 12px 10px 16px;
  background: #2b2320; color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
  font-size: 13px;
  animation: cw-update-in 180ms ease-out;
}
@keyframes cw-update-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.cw-update-banner-msg { white-space: nowrap; }
.cw-update-banner-reload {
  padding: 6px 14px; font-size: 13px; font-weight: 600;
  background: var(--accent-mid, #c47a68); color: #fff;
  border: none; border-radius: 6px; cursor: pointer;
}
.cw-update-banner-reload:hover { background: #d98b78; border-color: transparent; color: #fff; }
.cw-update-banner-dismiss {
  padding: 0 4px; font-size: 18px; line-height: 1;
  background: transparent; color: rgba(255, 255, 255, 0.7);
  border: none; cursor: pointer;
}
.cw-update-banner-dismiss:hover { background: transparent; color: #fff; border-color: transparent; }
@media (max-width: 480px) { .cw-update-banner-msg { white-space: normal; } }

/* Skill editor — the large hovering pane that replaced the cramped 10-row box
   the Skills rows used to expand inline. Sized well past the default .modal so a
   long skill is readable (the Academic Humanizer built-in is ~240 lines), and the
   textarea takes all leftover height so the pane grows with the viewport instead
   of scrolling a small box inside a narrow side pane. */
.modal.skill-editor-card {
  width: min(920px, 94vw); max-width: 920px;
  height: min(84vh, 940px); max-height: 84vh;
}
.skill-editor-body { display: flex; flex-direction: column; gap: 10px; min-height: 0; }
.skill-editor-desc { margin: 0; font-size: 12px; line-height: 1.5; color: #666; }
.skill-editor-desc:empty { display: none; }
#skill-editor-text {
  flex: 1; min-height: 0; width: 100%; box-sizing: border-box; resize: none;
  padding: 12px 14px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px; line-height: 1.6; color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 4px;
}
#skill-editor-text:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
#skill-editor-text[readonly] { background: #faf9f8; color: #333; }
.skill-editor-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
  padding: 10px 18px; border-top: 1px solid var(--border);
}

/* ---- Facelift (theme) — interaction states on chrome controls ----
   Hover tints for .tb-btn / .tb-menu-btn / .doc-menu-btn / .menu-item already
   exist above and recolor via --accent-light; only .icon-btn lacked one. The
   pressed states and the keyboard focus ring are new. Deliberately scoped to the
   toolbar / menu / header controls — the editor tabs and the document editing
   surfaces (.ProseMirror, CodeMirror) are intentionally left untouched. */
.icon-btn:hover { background: var(--accent-light); }
.doc-menu-btn:active, .tb-btn:active, .tb-menu-btn:active, .icon-btn:active,
.menu-item:active, .header-action-btn:active, .user-menu-btn:active {
  background: var(--accent-light); color: var(--accent-700);
}
.doc-menu-btn:focus-visible, .tb-btn:focus-visible, .tb-menu-btn:focus-visible,
.icon-btn:focus-visible, .menu-item:focus-visible,
.header-action-btn:focus-visible, .user-menu-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Seamless chrome buttons stay boxless even on hover. The generic `button:hover`
   (style.css ~194) sets border-color to the accent, which outranks each button's
   own base rule on hover and would flash a box back; this override keeps the border
   transparent so the soft --accent-light fill is the only hover cue (matches the
   homepage's ghost buttons). Compile/Send keep their fill via their own id rules. */
.doc-menu-btn:hover, .tb-btn:hover, .tb-menu-btn:hover, .icon-btn:hover,
.share-btn:hover, #header-grammar-btn:hover { border-color: transparent; }
.skill-editor-actions .skill-edit-status { margin-right: auto; font-size: 12px; color: #666; }
/* Download sits at the left of the footer, the Save / Reset pair at the right;
   the status text between them carries the margin-right:auto that separates the
   two groups, so the layout holds whether or not the edit actions are shown. */
.skill-editor-format { font-size: 12px; padding: 2px 4px; }
.skill-editor-edit-actions { display: flex; align-items: center; gap: 8px; }

/* "Opening…" overlay — instant feedback while openDoc tears down and rebuilds the editor
   (~2.4 s for a large manuscript), so a click never reads as "nothing happened". */
.doc-loading-overlay {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(248, 247, 244, 0.86);
  -webkit-backdrop-filter: blur(1.5px); backdrop-filter: blur(1.5px);
}
.doc-loading-overlay.hidden { display: none; }
.dlo-box { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.dlo-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid rgba(0, 0, 0, 0.14); border-top-color: #7a3b2e;
  animation: dlo-spin 0.8s linear infinite;
}
@keyframes dlo-spin { to { transform: rotate(360deg); } }
.dlo-text {
  font-size: 14px; color: #555; font-weight: 500;
  max-width: 70vw; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- Trial mode (unauthenticated, browser-local) ---------- */

/* The invitation bar, between the masthead and the editor. #app is a column
   flex, so flex-shrink:0 keeps it at its content height while #main takes the
   rest. Wraps rather than truncates: at a laptop width the legal line drops to
   its own row instead of pushing the button off-screen. */
.trial-banner {
  flex-shrink: 0;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px 14px;
  padding: 9px 18px;
  background: var(--surface2, #f6f3ee);
  border-bottom: 1px solid var(--text);
  font-family: var(--ui-font);
  font-size: 13px; color: var(--text);
}
.trial-banner.hidden { display: none; }
.trial-banner-msg { font-weight: 500; }
.trial-banner-btn {
  font-family: var(--ui-font); font-size: 13px; font-weight: 600;
  padding: 5px 13px; border-radius: 4px; cursor: pointer;
  background: var(--accent, #7a3b2e); color: #fff;
  border: 1px solid var(--accent, #7a3b2e);
}
.trial-banner-btn:hover { filter: brightness(1.08); }
.trial-banner-legal { font-size: 12px; color: var(--text-dim, #888); }
.trial-banner-legal a { color: inherit; }

/* The AI pane KEEPS its normal geometry in trial mode. Hiding it would hide the
   half of the product a visitor is deciding about; instead the tabs stay visible
   and switchable and this panel stands in for whichever one they pick, so the
   pane reads as a locked feature rather than a missing one. */
.chat-trial-lock {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px;
  padding: 28px 26px;
  text-align: center;
  font-family: var(--ui-font);
  color: var(--text);
}
.chat-trial-lock.hidden { display: none; }
.ctl-icon { font-size: 26px; line-height: 1; opacity: 0.75; }
.ctl-title { font-size: 16px; font-weight: 600; margin: 0; }
.ctl-blurb {
  font-size: 13.5px; line-height: 1.55; margin: 0;
  max-width: 34ch; color: var(--text-dim, #6b6b6b);
}
.ctl-cta {
  margin-top: 6px;
  font-family: var(--ui-font); font-size: 13px; font-weight: 600;
  padding: 7px 16px; border-radius: 4px; cursor: pointer;
  background: var(--accent, #7a3b2e); color: #fff;
  border: 1px solid var(--accent, #7a3b2e);
}
.ctl-cta:hover { filter: brightness(1.08); }
.ctl-foot { font-size: 12px; margin: 0; color: var(--text-dim, #8a8a8a); }

/* A locked tab still reads as a tab, but carries the padlock so the state is
   legible before it is clicked, not only after. Keyed off the tab's OWN state
   (chatTabLock, via .is-locked) rather than off trial mode, because a signed-in
   reader on a plan that does not cover a tab sees the same lock for the same
   reason. */
.chat-tab.is-locked::after {
  content: ' \1F512';
  font-size: 0.85em;
  opacity: 0.55;
}



/* Connect Word dialog. The path and the key are copy targets, so they are monospaced
   and allowed to wrap rather than overflow a narrow modal. */
.word-path { font: 12px ui-monospace, Menlo, monospace; background: #f4f5f7; border: 1px solid #e2e5ea;
             border-radius: 5px; padding: 7px 9px; margin: 6px 0 10px; overflow-wrap: anywhere; }
.word-key  { display: block; font: 12px ui-monospace, Menlo, monospace; background: #f4f5f7;
             border: 1px solid #e2e5ea; border-radius: 5px; padding: 8px 9px; margin: 8px 0 6px;
             overflow-wrap: anywhere; user-select: all; }
/* The two steps need to read as steps; without this the headings sit flush against the
   preceding paragraph and the dialog reads as one wall of text. */
#word-modal h4 { margin: 16px 0 6px; font-size: 13px; }
#word-modal .modal-body > p:first-child { margin-top: 0; }
#word-modal .modal-body { max-height: 70vh; overflow-y: auto; }
