/* ============================================================
   Lentium design tokens (from the brand mood board)
   Dark-mode-first, single deliberate identity -- no light theme.
   Fonts fall back to system sans until Inter/Satoshi are self-hosted
   as WOFF2 files in assets/fonts/ during Phase 2 asset work.
   ============================================================ */
:root {
  --bg: #0B0B0F;
  --surface-1: #121217; /* elevated */
  --surface-2: #1A1A20; /* raised more */
  --surface-3: #23232A; /* subtle */
  --border: #2A2A33;
  --divider: #1E1E28;
  --shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
  --inset: 0 1px 0 rgba(255, 255, 255, 0.05);

  --text-primary: #F2F2F5;
  --text-secondary: #A8A8B3;
  --text-tertiary: #6E6E7A;

  --primary: #8B7CF6;
  --primary-strong: #A395FF;
  --primary-wash: rgba(139, 124, 246, 0.16);
  --iridescent: linear-gradient(135deg, #6366F1 0%, #A78BFA 55%, #F472B6 100%);

  --link: #8B7CF6;
  --tag: #5B93F5;
  --highlight: #F5A623;
  --highlight-bg: rgba(245, 166, 35, 0.28);
  --success: #4ADE80;
  --warning: #F56B4A;
  --muted: #6E6E7A;

  /* Lens View: every non-center node shares this one on-brand light purple
     (kind is read off each label's own @ // ** >> -- prefix, not color);
     the center node is instead an animated iridescent orb (canvas-drawn,
     see app.js) cycling through --lens-orb-1/2/3. */
  --node-color: #B7ACFF;
  --lens-orb-1: #6EA8FF;
  --lens-orb-2: #9B7CF6;
  --lens-orb-3: #E58FD8;

  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-display: "Satoshi", var(--font-ui);
  --font-mono: ui-monospace, "SFMono-Regular", "Cascadia Code", Consolas, monospace;

  /* Note/entity/flag/date reading-content font choice (account menu) and
     size (floating slider) -- deliberately scoped to editor content only,
     never the app chrome, which stays on --font-ui/--font-display. */
  --font-content-sans: var(--font-ui);
  --font-content-serif: Georgia, "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif;
  --font-content-mono: var(--font-mono);
  --font-content-rounded: "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
  --content-font: var(--font-content-sans);
  --content-font-scale: 1;

  /* Paragraph spacing defaults -- unitless px counts (not lengths) so they
     can be multiplied by --content-font-scale in calc() below. Overridden
     per-user via inline custom properties set by applyHeadingSpacing() in
     app.js; these are just the factory defaults. Body text (Phase 14) is
     0/0 like a word processor's "No Spacing" style: paragraphs sit on
     consecutive lines unless the user spaces one explicitly, headings keep
     their own before/after. */
  --p-space-before: 0;
  --p-space-after: 0;
  --h1-space-before: 0;
  --h1-space-after: 18;
  --h2-space-before: 30;
  --h2-space-after: 10;
  --h3-space-before: 0;
  --h3-space-after: 14;

  color-scheme: dark;
}

[data-content-font="serif"] { --content-font: var(--font-content-serif); }
[data-content-font="mono"] { --content-font: var(--font-content-mono); }
[data-content-font="rounded"] { --content-font: var(--font-content-rounded); }

/* ---------- Light theme (Phase 6) -- app shell only, toggled via the
   account menu and applied server-side via <html data-theme> so there's
   no flash of the wrong theme. Not a naive inversion: neutrals keep a
   faint cool/purple bias to match the brand rather than going pure
   grey/white, and the accent hues are deepened from their dark-mode
   values for AA contrast against light surfaces. ---------- */
:root[data-theme="light"] {
  --bg: #FAFAFC;
  --surface-1: #FFFFFF;
  --surface-2: #F2F1F7;
  --surface-3: #E8E6F0;
  --border: #DDDAE6;
  --divider: #E8E6F0;
  --shadow: 0 8px 32px rgba(20, 16, 40, 0.10);
  --inset: 0 1px 0 rgba(0, 0, 0, 0.03);

  --text-primary: #17151F;
  --text-secondary: #57536A;
  --text-tertiary: #8B879C;

  --primary: #6D5BD0;
  --primary-strong: #5A47C4;
  --primary-wash: rgba(109, 91, 208, 0.10);

  --link: #6D5BD0;
  --tag: #3B6FD6;
  --highlight: #B36A00;
  --highlight-bg: rgba(245, 166, 35, 0.30);
  --success: #16A34A;
  --warning: #C43F23;
  --muted: #8B879C;

  /* deepened from the dark-mode values for AA contrast against light surfaces */
  --node-color: #8778D6;
  --lens-orb-1: #4E7FDB;
  --lens-orb-2: #7C63C9;
  --lens-orb-3: #C567AE;

  color-scheme: light;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
}
h1, h2, h3, p { margin: 0; }
a { color: var(--link); }
::selection { background: var(--primary-wash); }
:focus-visible { outline: 2px solid var(--primary-strong); outline-offset: 2px; }

/* ---------- Hero page shell (splash, login, register, reset) ---------- */
.hero-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  position: relative;
  overflow: hidden;
}
/* Restrained -- the mood board's decorative glow is used once, here, not scattered through the working app */
.hero-shell::before {
  content: "";
  position: absolute;
  top: -20%;
  right: -10%;
  width: 60vw;
  height: 60vw;
  max-width: 640px;
  max-height: 640px;
  background: radial-gradient(circle, rgba(139, 124, 246, 0.22), transparent 70%);
  filter: blur(10px);
  pointer-events: none;
}

.card {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow);
  padding: 36px 32px;
}
.card-wide { max-width: 720px; }

.brand-mark {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 28px;
}
.brand-mark .dot {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--iridescent);
  flex: none;
}
.brand-mark .wordmark {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}

.page-title { font-size: 24px; font-weight: 600; line-height: 32px; margin-bottom: 6px; }
.page-subtitle { font-size: 14px; color: var(--text-secondary); margin-bottom: 24px; }

/* ---------- Forms ---------- */
.field { margin-bottom: 18px; }
.field label {
  display: block;
  font-size: 13px;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.field input[type="email"],
.field input[type="password"],
.field input[type="text"] {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 14px;
  color: var(--text-primary);
  font-family: inherit;
}
.field input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-wash);
}
.field-hint { font-size: 12px; color: var(--text-tertiary); margin-top: 6px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 999px;
  padding: 10px 20px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
}
.btn-primary { background: var(--primary); color: #0B0B0F; width: 100%; }
.btn-primary:hover { background: var(--primary-strong); }
.btn-primary:active { transform: scale(0.98); }
.btn-ghost {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-primary);
}
.btn-ghost:hover { border-color: var(--primary); color: var(--primary-strong); }

.form-footer {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--divider);
  font-size: 13px;
  color: var(--text-secondary);
  text-align: center;
}

/* ---------- Landing page (item 6) -- wider hero for logged-out visitors,
   built on top of .hero-shell's existing glow rather than a new backdrop.
   Two columns (copy+form / animated graphic) above a 4-up feature strip;
   both stack to one column under 860px. ---------- */
.hero-shell.landing { align-items: flex-start; padding-top: 64px; padding-bottom: 64px; }
.landing-wrap { position: relative; width: 100%; max-width: 1080px; margin: 0 auto; }
.landing-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px); gap: 56px; align-items: center;
  margin-bottom: 64px;
}
.landing-copy .page-title {
  font-family: var(--font-display); font-size: 40px; line-height: 1.12; font-weight: 600;
  letter-spacing: -0.01em; margin-bottom: 16px; max-width: 20ch;
}
.landing-copy .page-subtitle { font-size: 16px; line-height: 1.6; max-width: 46ch; margin-bottom: 28px; }
.landing-form {
  display: flex; gap: 8px; max-width: 420px; margin-bottom: 10px;
}
.landing-form input[type="email"] {
  flex: 1; min-width: 0; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 12px 14px; font-size: 14px; color: var(--text-primary); font-family: inherit;
}
.landing-form input[type="email"]:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-wash), 0 0 18px rgba(139, 124, 246, 0.25);
}
.landing-form .btn { width: auto; white-space: nowrap; padding: 12px 22px; }
.landing-form-hint { font-size: 12px; color: var(--text-tertiary); margin-bottom: 28px; }
.landing-login-link { font-size: 13px; color: var(--text-secondary); }
.landing-login-link a { color: var(--primary-strong); text-decoration: none; }
.landing-login-link a:hover { text-decoration: underline; }

.landing-confirm { display: flex; align-items: flex-start; gap: 12px; max-width: 420px; margin-bottom: 28px; }
.landing-confirm-dot {
  flex: none; width: 32px; height: 32px; border-radius: 50%; background: var(--primary-wash);
  color: var(--primary-strong); display: flex; align-items: center; justify-content: center;
}
.landing-confirm-dot svg { width: 16px; height: 16px; }
.landing-confirm strong { display: block; color: var(--text-primary); font-size: 14px; margin-bottom: 2px; }
.landing-confirm span { font-size: 13px; color: var(--text-secondary); }

/* The graphic: a small constellation of chip-colored nodes gently drifting
   and connected by lines, echoing the real Lens View graph rather than
   generic decoration -- the exact chip colors are the same --chip-* tokens
   the working app uses for @entity/#flag/**date/>>note/--marker. */
.landing-graphic { position: relative; width: 100%; aspect-ratio: 1 / 1; max-width: 420px; margin: 0 auto; }
.landing-graphic svg { width: 100%; height: 100%; overflow: visible; }
.lg-edge { stroke: var(--border); stroke-width: 1.4; }
.lg-node-glow { animation: lgPulse 3.6s ease-in-out infinite; transform-origin: center; }
.lg-node-center { fill: url(#lgCenterGradient); }
.lg-node circle:not(.lg-node-center) { stroke-width: 1.5; }
.lg-node { animation: lgFloat 7s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
.lg-node:nth-of-type(2) { animation-delay: -1.1s; }
.lg-node:nth-of-type(3) { animation-delay: -2.6s; }
.lg-node:nth-of-type(4) { animation-delay: -4.2s; }
.lg-node:nth-of-type(5) { animation-delay: -5.5s; }
@keyframes lgFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}
@keyframes lgPulse {
  0%, 100% { opacity: 0.55; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.12); }
}

.feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.feature-card {
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 12px; padding: 20px 18px;
  opacity: 0; animation: featureFadeUp 0.5s ease forwards;
}
.feature-card:nth-child(1) { animation-delay: 0.05s; }
.feature-card:nth-child(2) { animation-delay: 0.15s; }
.feature-card:nth-child(3) { animation-delay: 0.25s; }
.feature-card:nth-child(4) { animation-delay: 0.35s; }
@keyframes featureFadeUp {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
.feature-icon {
  width: 34px; height: 34px; border-radius: 9px; display: flex; align-items: center; justify-content: center;
  margin-bottom: 14px;
}
.feature-icon svg { width: 17px; height: 17px; }
.feature-card h3 { font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 6px; }
.feature-card p { font-size: 13px; line-height: 1.55; color: var(--text-secondary); }

@media (max-width: 860px) {
  .landing-grid { grid-template-columns: 1fr; gap: 36px; }
  .landing-graphic { max-width: 280px; }
  .feature-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
  .landing-form { flex-direction: column; }
  .feature-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .lg-node, .lg-node-glow, .feature-card { animation: none; opacity: 1; }
}

/* ---------- Flash messages ---------- */
.flash {
  border-radius: 8px;
  padding: 12px 14px;
  font-size: 13px;
  margin-bottom: 18px;
  border: 1px solid transparent;
}
.flash-success { background: rgba(74, 222, 128, 0.1); border-color: rgba(74, 222, 128, 0.3); color: var(--success); }
.flash-error   { background: rgba(245, 107, 74, 0.1); border-color: rgba(245, 107, 74, 0.3); color: var(--warning); }
.flash-info    { background: var(--primary-wash); border-color: rgba(139, 124, 246, 0.3); color: var(--primary-strong); }

/* ---------- Simple admin table (invites) ---------- */
.table-wrap { overflow-x: auto; }
table.simple { width: 100%; border-collapse: collapse; font-size: 13px; }
table.simple th {
  text-align: left;
  color: var(--text-tertiary);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
table.simple td {
  padding: 10px;
  border-bottom: 1px solid var(--divider);
  color: var(--text-primary);
}
.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
}
.pill-pending { background: var(--primary-wash); color: var(--primary-strong); }
.pill-accepted { background: rgba(74, 222, 128, 0.12); color: var(--success); }
.pill-revoked { background: rgba(110, 110, 122, 0.16); color: var(--muted); }

.top-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 28px;
  border-bottom: 1px solid var(--divider);
}
.page-wrap { max-width: 880px; margin: 0 auto; padding: 32px 24px 64px; }

/* ============================================================
   IDE shell (app.php) -- Phase 2: explorer, tabs, editor.
   No outline column / entity-flag-date chip colors yet (Phase 3),
   no drag/context-menu affordances yet (Phase 4).
   ============================================================ */
.app {
  position: fixed; inset: 0; min-height: 480px; display: grid;
  grid-template-columns: 44px 240px 1fr;
  grid-template-rows: minmax(0, 1fr) 24px;
  grid-template-areas: "activity side main" "activity side status";
  background: var(--bg);
  overflow: hidden;
}

/* ---------- Activity bar ---------- */
.activity-bar {
  grid-area: activity; background: var(--surface-1); border-right: 1px solid var(--border);
  display: flex; flex-direction: column; align-items: center; padding: 10px 0; gap: 4px;
}
.abtn {
  width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--text-tertiary); border-radius: 7px; cursor: pointer;
  position: relative;
}
.abtn svg { width: 18px; height: 18px; }
.abtn:hover { color: var(--text-primary); }
.abtn.active { color: var(--text-primary); background: var(--primary-wash); }
.abtn.active::before {
  content: ""; position: absolute; left: -10px; top: 5px; bottom: 5px; width: 2.5px;
  background: var(--primary-strong); border-radius: 2px;
}
.activity-spacer { flex: 1; }
.account-menu-wrap { position: relative; }
.account-dot { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; display: block; }
.account-menu {
  position: absolute; bottom: 4px; left: 40px; min-width: 190px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow); padding: 6px; z-index: 40;
  display: none;
}
.account-menu.open { display: block; }
.account-email {
  padding: 8px 10px 6px; font-size: 12px; color: var(--text-tertiary); border-bottom: 1px solid var(--divider);
  margin-bottom: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.account-menu-item {
  display: block; width: 100%; padding: 7px 10px; border-radius: 6px; font-size: 13px; color: var(--text-primary);
  text-decoration: none; text-align: left; background: none; border: none; font-family: inherit; cursor: pointer;
}
.account-form { margin: 0; padding: 0; }
.account-menu-item:hover { background: var(--surface-3); }
.account-font-row { padding: 6px 10px 8px; }
.account-font-label { display: block; font-size: 11px; color: var(--text-tertiary); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.04em; }
.font-pill-group { display: flex; gap: 4px; }
.font-pill {
  flex: 1; padding: 6px 0; border-radius: 6px; border: 1px solid var(--border); background: var(--surface-3);
  color: var(--text-secondary); font-size: 14px; line-height: 1; cursor: pointer; text-align: center;
}
.font-pill[data-font="serif"] { font-family: var(--font-content-serif); }
.font-pill[data-font="mono"] { font-family: var(--font-content-mono); }
.font-pill[data-font="rounded"] { font-family: var(--font-content-rounded); }
.font-pill:hover { border-color: var(--primary); color: var(--text-primary); }
.font-pill.active { background: var(--primary-wash); border-color: var(--primary); color: var(--primary-strong); }
.date-format-pill { font-size: 11px; font-weight: 600; letter-spacing: 0.03em; font-family: var(--font-ui); }

/* ---------- Side panel ---------- */
.side { grid-area: side; background: var(--surface-1); border-right: 1px solid var(--border); min-width: 0; position: relative; overflow: visible; transition: border-color 0.15s; }
.side.collapsed { border-color: transparent; cursor: pointer; }
/* Collapsed panes shrink to a thin sliver (see the JS grid-template-columns
   math) rather than vanishing to 0 -- the pane's own background paints that
   sliver automatically; only its inner content needs hiding so it doesn't
   spill into the main column. */
.side.collapsed .side-inner, .side.collapsed .side-lens-mount { visibility: hidden; }
.side.collapsed:hover { animation: paneSliverBounceRight 0.5s ease; }

.outline.collapsed { cursor: pointer; }
.outline.collapsed .outline-inner { visibility: hidden; }
.outline.collapsed:hover { animation: paneSliverBounceLeft 0.5s ease; }

@keyframes paneSliverBounceRight {
  0%, 100% { transform: translateX(0); }
  35% { transform: translateX(3px); }
  65% { transform: translateX(-1px); }
}
@keyframes paneSliverBounceLeft {
  0%, 100% { transform: translateX(0); }
  35% { transform: translateX(-3px); }
  65% { transform: translateX(1px); }
}

/* Drag-to-resize width handle -- a thin invisible strip along the pane's
   outer edge, distinct from the round collapse button that already lives
   there (this spans the full height; the button stays a small circle). */
.pane-width-handle { position: absolute; top: 0; bottom: 0; width: 6px; cursor: col-resize; z-index: 4; }
.pane-width-handle-right { right: -3px; }
.pane-width-handle-left { left: -3px; }
.pane-width-handle:hover, .pane-width-handle.dragging { background: var(--primary-wash); }

.edge-collapse-btn {
  position: absolute; top: 10px; width: 18px; height: 18px; border: 1px solid var(--primary);
  background: var(--primary-wash); color: var(--primary-strong); border-radius: 50%;
  display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 5;
}
.edge-collapse-btn:hover { color: var(--bg); background: var(--primary-strong); border-color: var(--primary-strong); }
.edge-collapse-btn.flipped { transform: rotate(180deg); }
.edge-collapse-left { right: -9px; }
.edge-collapse-right { left: -9px; }
.side-inner { height: 100%; display: flex; flex-direction: column; overflow: hidden; }
/* Item 3: hosts the relocated #lensPane while nothing is open -- same
   height/flex shape as .side-inner so #lensPane (flex: 1 1 45%) fills it. */
.side-lens-mount { height: 100%; display: flex; flex-direction: column; }
.side-lens-mount .lens-pane-header { padding-left: 14px; }
/* Explorer density (compact). One place to retune every metric that decides
   how much tree fits on screen -- row height, the indent each nesting level
   adds, and the gap that separates root groups. */
.side-inner { --tree-row-h: 21px; --tree-indent: 14px; --tree-group-gap: 3px; }
.side-filter {
  position: relative;
  display: flex; align-items: center; gap: 6px; margin: 8px 8px 6px; padding: 5px 8px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--surface-2); color: var(--text-tertiary);
}
.side-filter svg { width: 14px; height: 14px; flex: none; }
.side-filter input {
  flex: 1; min-width: 0; border: none; background: transparent; color: var(--text-primary); font: inherit; font-size: 12.5px; outline: none;
}
.side-filter input::placeholder { color: var(--text-tertiary); }
.side-filter-clear { border: none; background: transparent; color: var(--text-tertiary); cursor: pointer; display: flex; padding: 0; flex: none; }
.side-filter-clear svg { width: 12px; height: 12px; }
.side-filter-clear:hover { color: var(--text-primary); }
/* Filter results are a flat list, so each hit shows the folder path it lives
   in -- without it, two same-named notes are indistinguishable and there's no
   way to tell where a match actually is. */
.filter-hit { margin-bottom: 2px; }
.filter-crumb {
  padding: 0 8px 3px 27px; font-size: 10.5px; color: var(--text-tertiary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.filter-hit .tree-row[data-reveal-folder] { font-weight: 600; }
/* Refs-pane filter sits under its own header, so it needs tighter margins
   than the explorer's (which has the whole pane to breathe in). */
.side-filter-ref { margin: 0 10px 6px; padding: 4px 7px; }
.side-filter-ref input { font-size: 12px; }
.side-filter-date { border: none; background: transparent; color: var(--text-tertiary); cursor: pointer; display: flex; padding: 0; flex: none; }
.side-filter-date svg { width: 14px; height: 14px; }
.side-filter-date:hover { color: var(--primary-strong); }
/* The real date input only exists to summon the native picker -- the visible
   affordance is the calendar button next to it. */
.side-filter-date-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; border: none; padding: 0; }
.icon-btn {
  border: none; background: transparent; color: var(--text-tertiary); cursor: pointer; width: 22px; height: 22px;
  border-radius: 5px; display: flex; align-items: center; justify-content: center;
}
.icon-btn svg { width: 14px; height: 14px; }
.icon-btn:hover { background: var(--surface-3); color: var(--text-primary); }

/* top half: folder/note tree, a resizable pane (55/45 default split via flex-basis; a drag sets an explicit height instead) */
.side-notes { overflow-y: auto; padding: 0 6px 6px; flex: 1 1 55%; min-height: 100px; }

.side-resize-handle {
  flex: none; height: 7px; display: flex; align-items: center; justify-content: center; cursor: row-resize;
  color: var(--text-tertiary); border-top: 1px solid var(--divider); border-bottom: 1px solid var(--divider);
}
.side-resize-handle:hover, .side-resize-handle:active { color: var(--text-secondary); background: var(--surface-3); }
.side-resize-handle svg { width: 14px; height: 14px; pointer-events: none; }

/* bottom half: switchable Entities/Flags/Dates list. The type switcher IS the
   header -- which list is showing is exactly what a title row would say, so
   the two were saying the same thing on two separate rows (plus an icon strip
   pinned to the pane's bottom edge). */
.side-ref-pane { flex: 1 1 45%; min-height: 100px; display: flex; flex-direction: column; }
.side-ref-header {
  flex: none; display: flex; align-items: center; gap: 5px; padding: 5px 8px 4px;
}
.side-ref-switch { display: flex; gap: 2px; }
.side-ref-title {
  margin-left: 3px; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.side-ref-count { margin-left: auto; font-family: var(--font-mono); font-size: 11px; font-weight: 400; color: var(--text-tertiary); }
.side-ref-collapse-btn {
  flex: none; width: 20px; height: 20px; border-radius: 5px; border: none; background: transparent;
  color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer;
  margin-left: 2px;
}
.side-ref-collapse-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.side-ref-collapse-btn svg { width: 13px; height: 13px; transition: transform 0.12s; }
.side-ref-pane.collapsed .side-ref-collapse-btn svg { transform: rotate(-90deg); }
/* Collapsed: only the header row (with the switcher, so the user can still
   pick a section and un-collapse) stays -- filter + list hide, and the
   pane stops claiming a flex share, so the tree above grows to fill the
   whole vertical space instead of leaving an empty gap below it. */
.side-ref-pane.collapsed { flex: none; }
.side-ref-pane.collapsed .side-filter-ref,
.side-ref-pane.collapsed .side-ref-list { display: none; }
/* The drag handle between the two halves has nothing to resize against
   once the bottom half is collapsed. */
.side-resize-handle.pane-collapsed-hide { display: none; }
.side-ref-list { flex: 1; overflow-y: auto; padding: 0 6px 6px; min-height: 0; }
.side-ref-icon-btn {
  width: 24px; height: 24px; border-radius: 5px; border: none; background: transparent; color: var(--text-tertiary);
  cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.side-ref-icon-btn svg { width: 13px; height: 13px; }
.side-ref-icon-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.side-ref-icon-btn.active { background: var(--primary-wash); color: var(--primary-strong); }

/* Bottom action bar: create actions (which used to be two unlabelled icons in
   a header row) plus the explorer's own settings, anchored where the eye ends
   up rather than where it starts. */
.side-actions {
  flex: none; display: flex; align-items: center; gap: 6px; padding: 5px 8px;
  border-top: 1px solid var(--divider);
}
.side-new-btn {
  display: flex; align-items: center; gap: 6px; border: none; background: transparent; cursor: pointer;
  color: var(--primary-strong); font-family: inherit; font-size: 12.5px; font-weight: 600;
  padding: 4px 8px; border-radius: 6px;
}
.side-new-btn svg { width: 14px; height: 14px; flex: none; }
.side-new-btn:hover { background: var(--primary-wash); }
.side-actions .icon-btn { margin-left: auto; }

.tree-row {
  display: flex; align-items: center; gap: 5px; height: var(--tree-row-h); padding: 0 8px;
  border-radius: 5px; cursor: pointer;
  font-size: 12.5px; color: var(--text-secondary); user-select: none; white-space: nowrap;
}
.tree-row:hover { background: var(--surface-3); color: var(--text-primary); }
/* The open document: wash + a brand-coloured label. No left accent bar -- next
   to the guide rail's tick it read as a stray "{" hanging off the row. */
.tree-row.active-doc {
  background: var(--primary-wash); color: var(--primary-strong); font-weight: 600;
}
.tree-row.active-doc .row-icon { color: var(--primary-strong); }
/* Which folder a new note/folder/upload will land in -- deliberately NOT the
   brand wash. Only one row in the tree may read as "this is what's open", and
   that is the active document. Sharing active-doc's styling meant a folder
   clicked once kept a purple highlight while the user moved on to a note in a
   different folder: two rows both claiming to be the focus. Quieter than
   hover, so the hierarchy reads selected < hover < open. */
.tree-row.selected-folder { background: var(--surface-2); color: var(--text-primary); }
.tree-row.selected-folder:hover { background: var(--surface-3); }
/* Chevron rides the right-hand rail (see renderFolderNode): pointing down when
   the folder is open, right when it's closed. Rotations are stated for BOTH
   states rather than only the collapsed one, so the resting state can't be
   left at whatever the raw glyph happens to be. */
.tree-row .chev {
  width: 11px; height: 11px; flex: none; margin-left: auto; color: var(--text-tertiary);
  transform: rotate(90deg); transition: transform 0.12s;
}
.tree-row.collapsed .chev { transform: rotate(0deg); }
.tree-row:hover .chev { color: var(--text-secondary); }
.tree-row .row-icon { width: 14px; height: 14px; flex: none; }
.tree-row svg { width: 14px; height: 14px; flex: none; } /* fallback for icons that don't carry their own .row-icon class, e.g. the attachment row's paperclip */
.tree-row .row-label { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.tree-row.folder-row .row-label { font-weight: 600; }
.tree-row.attachment-row { color: var(--text-tertiary); }
.tree-row.attachment-row:hover { color: var(--text-primary); }

/* Touch-only row action: a touch tap can't right-click, so on a coarse
   (touch) pointer a note row gets an explicit "..." button that opens the
   identical context menu. Hidden on mouse/trackpad devices, which already
   have right-click -- desktop gets no visual change at all. margin-left:auto
   pushes it to the row's right edge regardless of the share badge before it. */
.row-more-btn {
  display: none; flex: none; margin-left: auto; padding: 3px; border: none; background: none;
  color: var(--text-tertiary); cursor: pointer; border-radius: 4px;
}
.row-more-btn svg { width: 15px; height: 15px; }
.row-more-btn:hover, .row-more-btn:active { color: var(--text-primary); background: var(--surface-2); }
@media (pointer: coarse) {
  .row-more-btn { display: flex; }
}

/* Depth is nesting, not padding: one indent + one guide rail per level, with
   a short tick into each row. Before this, a row's depth could only be judged
   by comparing its left edge to its neighbours' -- and it lied, because the
   inline chevron on folder rows pushed their icons a level's worth to the
   right, so a note read as a sibling of its own folder. */
.tree-children { margin-left: var(--tree-indent); border-left: 1px solid var(--border); }
.tree-children > .tree-row { position: relative; }
.tree-children > .tree-row::before {
  content: ""; position: absolute; left: 0; top: 50%; width: 5px; height: 1px; background: var(--border);
}
.tree-children.collapsed { display: none; }
/* Root folders read as sections: brand-tinted icon, primary text, and a small
   gap between groups so the tree has a rhythm rather than one flat run. */
.side-notes > .tree-row.folder-row { color: var(--text-primary); }
.side-notes > .tree-row.folder-row .row-icon { color: var(--primary); }
.side-notes > .tree-children + .tree-row { margin-top: var(--tree-group-gap); }
.indent-1 { padding-left: 22px; }
.explorer-empty { padding: 16px 12px; font-size: 12px; line-height: 1.6; color: var(--text-tertiary); }
.tree-row.hidden-item { opacity: 0.5; }
.tree-row.hidden-item:hover { opacity: 0.8; }

/* Phase 15 -- sharing. The badge marks a shared note in the tree (incoming:
   plain tertiary; outgoing: brand tint); details live in the row tooltip. */
.share-badge { flex: none; display: inline-flex; margin-left: 2px; color: var(--text-tertiary); }
.share-badge svg { width: 11px; height: 11px; }
.share-badge-owner { color: var(--primary); }
.tree-row.active-doc .share-badge { color: var(--primary-strong); }

/* Share dialog */
.share-add-row { display: flex; gap: 8px; }
.share-add-row input { flex: 1; min-width: 0; }
.share-add-row .btn { flex: none; width: auto; padding: 8px 16px; }
.share-error { margin-top: 7px; font-size: 12.5px; color: var(--warning); }
.share-list { display: flex; flex-direction: column; gap: 4px; }
.share-row {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  background: var(--surface-3); border: 1px solid var(--border); border-radius: 7px;
}
.share-row-email { flex: 1; min-width: 0; font-size: 13px; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; }
.share-revoke {
  flex: none; border: none; background: transparent; color: var(--text-tertiary); cursor: pointer;
  font-size: 15px; line-height: 1; padding: 2px 6px; border-radius: 5px;
}
.share-revoke:hover { color: var(--warning); background: rgba(245, 107, 74, 0.12); }

/* A >>link inside a shared note pointing at a note the viewer can't access:
   present but clearly not a door -- muted, dashed, click explains. */
.chip.chip-foreign { opacity: 0.55; border-style: dashed; cursor: help; }

/* drag & drop -- moving notes in the explorer */
.tree-row[draggable] { cursor: grab; }
.tree-row[draggable]:active { cursor: grabbing; }
.tree-row.drag-over { background: var(--primary-wash); }
.tree-row.drag-over-before { box-shadow: inset 0 2px 0 var(--primary-strong); }
.tree-row.drag-over-after { box-shadow: inset 0 -2px 0 var(--primary-strong); }
.side-notes.drag-over-root { background: var(--primary-wash); border-radius: 6px; }

/* right-click context menu (rename / delete) */
.context-menu {
  position: fixed; z-index: 75; min-width: 170px; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 8px; box-shadow: var(--shadow); padding: 5px; font-family: var(--font-ui); display: none;
}
.context-menu.open { display: block; }
.context-menu-item { display: flex; align-items: center; gap: 9px; padding: 7px 10px; border-radius: 6px; cursor: pointer; font-size: 13px; color: var(--text-primary); }
.context-menu-item:hover { background: var(--surface-3); }
.context-menu-item.danger { color: var(--warning); }
.context-menu-item svg { width: 14px; height: 14px; flex: none; }
.context-menu-sep { height: 1px; background: var(--border); margin: 4px 6px; }

/* entities/flags/dates sidebar list views */
.list-row { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 6px; cursor: pointer; margin: 1px 4px; }
.list-row:hover { background: var(--surface-3); }
.list-row.active-doc { background: var(--primary-wash); }
.list-row .lbl-wrap { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.list-row .lbl { font-size: 12.5px; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row .sub { font-size: 10.5px; color: var(--text-tertiary); }
.list-row .count { margin-left: auto; font-size: 10.5px; color: var(--text-tertiary); font-family: var(--font-mono); }
.ent-dot, .flag-dot, .date-dot {
  width: 22px; height: 22px; border-radius: 6px; flex: none; display: flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 700;
}
.ent-dot { background: var(--chip-entity-wash); color: var(--chip-entity); border: 1px solid var(--chip-entity-border); }
.flag-dot { background: var(--chip-flag-wash); color: var(--chip-flag); border: 1px solid var(--chip-flag-border); font-family: var(--font-mono); font-size: 12px; }
.date-dot { background: var(--chip-date-wash); color: var(--chip-date); border: 1px solid var(--chip-date-border); }
.date-dot svg { width: 13px; height: 13px; }

/* ---------- Main / editor ---------- */
.main { grid-area: main; display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--bg); }

.note-breadcrumb {
  display: none; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 14px; font-size: 11.5px; color: var(--text-tertiary);
  background: var(--bg); border-bottom: 1px solid var(--divider); flex: none;
}
.note-breadcrumb.visible { display: flex; }
.crumb-path { display: flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; }
.crumb-seg { cursor: pointer; border-radius: 4px; padding: 1px 3px; }
.crumb-seg:hover { color: var(--text-primary); background: var(--surface-3); }
.crumb-seg-static { cursor: default; }
.crumb-seg-static:hover { color: inherit; background: none; }
.crumb-sep { display: inline-flex; color: var(--text-tertiary); }
.crumb-sep svg { width: 9px; height: 9px; }
.crumb-current { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.editor-mode-toggle { display: flex; align-items: center; gap: 2px; flex: none; background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px; padding: 2px; }
.emt-btn { width: 22px; height: 20px; border: none; border-radius: 5px; background: transparent; color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer; }
.emt-btn svg { width: 13px; height: 13px; }
.emt-btn:hover { color: var(--text-primary); }
.emt-btn.active { background: var(--primary-wash); color: var(--primary-strong); }

.tab-bar-wrap { position: relative; flex: none; }
.tab-bar {
  display: flex; align-items: stretch; background: var(--surface-2); border-bottom: 1px solid var(--border);
  overflow-x: auto; flex: none; min-height: 34px;
  scrollbar-width: none; /* item 22 -- scroll buttons replace the native scrollbar, not both at once */
}
.tab-bar::-webkit-scrollbar { display: none; }
.tab-scroll-btn {
  display: none; position: absolute; top: 0; bottom: 0; width: 22px; z-index: 6; align-items: center; justify-content: center;
  border: none; background: var(--surface-2); color: var(--text-secondary); cursor: pointer;
}
.tab-scroll-btn svg { width: 13px; height: 13px; }
.tab-scroll-btn:hover { color: var(--text-primary); background: var(--surface-3); }
.tab-bar-wrap.overflowing .tab-scroll-btn { display: flex; }
.tab-scroll-left { left: 0; border-right: 1px solid var(--border); box-shadow: 4px 0 6px -4px rgba(0, 0, 0, 0.3); }
.tab-scroll-right { right: 0; border-left: 1px solid var(--border); box-shadow: -4px 0 6px -4px rgba(0, 0, 0, 0.3); }
.tab {
  display: flex; align-items: center; gap: 7px; padding: 0 8px 0 12px; font-size: 12.5px; color: var(--text-secondary);
  border-right: 1px solid var(--border); cursor: pointer; white-space: nowrap; position: relative; max-width: 200px;
}
.tab .tlabel { overflow: hidden; text-overflow: ellipsis; }
.tab.active { background: var(--bg); color: var(--text-primary); }
.tab.active::after { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: var(--primary-strong); }
.tab .close {
  width: 16px; height: 16px; border-radius: 4px; display: flex; align-items: center; justify-content: center;
  flex: none; color: var(--text-tertiary); margin-left: 2px;
}
.tab .close:hover { background: var(--border); color: var(--text-primary); }
.tab .save-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--text-tertiary); transition: background-color .15s; }
.tab .save-dot[data-state="unsaved"] { background: var(--highlight); }
.tab .save-dot[data-state="saving"] { background: var(--primary-strong); animation: pulseDot 1s ease-in-out infinite; }
.tab .save-dot[data-state="saved"] { background: var(--success); }
.tab .save-dot[data-state="error"] { background: var(--warning); }
@keyframes pulseDot { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.tab[draggable] { cursor: grab; }
.tab[draggable]:active { cursor: grabbing; }
.tab.drag-over-before { box-shadow: inset 2px 0 0 var(--primary-strong); }
.tab.drag-over-after { box-shadow: inset -2px 0 0 var(--primary-strong); }

.toolbar {
  display: flex; align-items: center; gap: 2px; padding: 6px 10px; background: var(--bg);
  border-bottom: 1px solid var(--divider); flex: none; flex-wrap: wrap;
}
.tb-btn {
  width: 27px; height: 27px; border-radius: 5px; border: none; background: transparent; color: var(--text-secondary);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.tb-btn svg { width: 15px; height: 15px; }
.tb-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.tb-btn.active { background: var(--primary-wash); color: var(--primary-strong); }
.spacing-dropdown-wrap { position: relative; }
/* Phase 14 -- Word-style "Spacing Before / After" popover. */
.spacing-menu {
  position: absolute; top: 32px; left: 0; width: 240px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px; box-shadow: var(--shadow); padding: 10px; z-index: 40;
  display: none;
}
.spacing-menu.open { display: block; }
.spacing-menu-title { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 8px; }
.spacing-menu-fields { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.spacing-field { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text-secondary); }
.spacing-field span { width: 46px; }
.spacing-field input {
  width: 72px; padding: 4px 6px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface-1);
  color: var(--text-primary); font-family: var(--font-mono); font-size: 12.5px;
}
.spacing-field input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-wash); }
.spacing-field input::placeholder { color: var(--text-tertiary); font-family: var(--font-ui); font-style: italic; }
.spacing-field em { font-style: normal; color: var(--text-tertiary); font-size: 11px; }
.spacing-menu-presets { display: flex; flex-wrap: wrap; gap: 4px; }
.spacing-menu-item {
  padding: 4px 9px; border-radius: 999px; font-size: 12px; color: var(--text-primary);
  background: var(--surface-3); border: 1px solid transparent; font-family: inherit; cursor: pointer;
}
.spacing-menu-item:hover { border-color: var(--primary); color: var(--primary-strong); }
.spacing-menu-item[data-space-reset] { background: none; border-color: var(--border); color: var(--text-secondary); }
.spacing-menu-hint { margin-top: 8px; font-size: 11px; line-height: 1.4; color: var(--text-tertiary); }
.tb-sep { width: 1px; align-self: stretch; background: var(--border); margin: 3px 5px; }
.tb-hint { margin-left: auto; font-size: 11px; color: var(--text-tertiary); display: flex; gap: 12px; }
.tb-hint kbd { font-family: var(--font-mono); background: var(--surface-3); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; font-size: 10.5px; }

/* search-result navigation banner (appears after selecting a Shift-Shift search result) */
.search-banner { display: none; align-items: center; gap: 8px; padding: 8px 12px; background: var(--surface-2); border-bottom: 1px solid var(--divider); flex: none; }
.search-banner.open { display: flex; }
.sbn-query { font-size: 11px; color: var(--text-tertiary); white-space: nowrap; max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
.sbn-nav-btn { width: 26px; height: 26px; border-radius: 6px; border: none; background: transparent; color: var(--text-secondary); display: flex; align-items: center; justify-content: center; cursor: pointer; flex: none; }
.sbn-nav-btn svg { width: 13px; height: 13px; }
.sbn-nav-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.sbn-nav-btn:disabled { opacity: 0.35; cursor: default; }
.sbn-nav-btn:disabled:hover { background: transparent; color: var(--text-secondary); }
.sbn-viewport { flex: 1; min-width: 0; overflow: hidden; }
.sbn-track { display: flex; gap: 8px; transition: transform 0.2s ease; }
.sbn-card { flex: none; width: 190px; padding: 6px 9px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface-3); cursor: pointer; display: flex; flex-direction: column; gap: 2px; }
.sbn-card:hover { border-color: var(--text-tertiary); }
.sbn-card.active { background: var(--primary-wash); border-color: var(--primary); }
.sbn-card-head { display: flex; align-items: center; gap: 5px; }
.sbn-card-icon { width: 13px; height: 13px; flex: none; display: flex; align-items: center; justify-content: center; }
.sbn-card-title { font-size: 11.5px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sbn-card.active .sbn-card-title { color: var(--text-primary); }
.sbn-card-snippet { font-size: 11px; color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sbn-card mark { background: var(--primary-wash); color: var(--primary-strong); border-radius: 2px; padding: 0 1px; }
.sbn-counter { font-family: var(--font-mono); font-size: 11px; color: var(--text-tertiary); white-space: nowrap; }
.sbn-close-btn { width: 22px; height: 22px; border-radius: 6px; border: none; background: transparent; color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer; flex: none; }
.sbn-close-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.sbn-close-btn svg { width: 10px; height: 10px; }
.search-match-overlay { position: absolute; pointer-events: none; background: var(--primary-wash); box-shadow: 0 0 0 2px var(--primary-wash); border-radius: 2px; z-index: 2; }

/* Fuse-burn: on landing on a search result, a glowing line traces the
   overlay's rounded border (with a bright traveling spark) before fading
   out, leaving the overlay's own background as the settled highlight --
   .search-match-overlay is already position:absolute, so it's a valid
   containing block for this child with no extra positioning needed. */
.fuse-svg { position: absolute; inset: -7px; width: calc(100% + 14px); height: calc(100% + 14px); overflow: visible; opacity: 1; }
.fuse-svg.fuse-fading { opacity: 0; transition: opacity .35s ease; }
.fuse-path {
  fill: none; stroke: url(#fuseGrad); stroke-width: 2; stroke-linecap: round;
  filter: drop-shadow(0 0 3px var(--primary)) drop-shadow(0 0 9px var(--primary-strong));
}
.fuse-path.fuse-tracing { animation: fuse-burn .55s cubic-bezier(.3, .8, .4, 1) forwards; }
@keyframes fuse-burn {
  from { stroke-dashoffset: 100; }
  to   { stroke-dashoffset: 0; }
}
.fuse-spark { filter: drop-shadow(0 0 4px #fff) drop-shadow(0 0 10px var(--primary-strong)); }

.editor-scroll { position: relative; flex: 1; overflow-y: auto; padding: 36px 24px 120px; }

/* Text-size slider -- fixed horizontally in the breadcrumb bar, just left of
   the Page/Fill toggle (see .crumb-right below); previously a floating
   vertical control docked over the editor scroll area. */
.crumb-right { display: flex; align-items: center; gap: 10px; flex: none; }
.font-scale-control { display: flex; align-items: center; gap: 6px; }
.fsc-icon { color: var(--text-tertiary); line-height: 1; user-select: none; }
.fsc-icon-sm { font-size: 10px; }
.fsc-icon-lg { font-size: 13px; }
.fsc-track { position: relative; width: 64px; height: 4px; border-radius: 999px; background: var(--surface-3); cursor: pointer; }
.fsc-fill { position: absolute; top: 0; left: 0; height: 100%; border-radius: 999px; background: var(--iridescent); }
.fsc-thumb {
  position: absolute; top: 50%; width: 12px; height: 12px; border-radius: 50%; background: var(--primary);
  box-shadow: 0 0 0 2px var(--bg); transform: translate(-50%, -50%); cursor: grab; transition: transform 0.1s ease;
}
.fsc-thumb:hover, .fsc-thumb:focus-visible { transform: translate(-50%, -50%) scale(1.15); }
.fsc-thumb:active { cursor: grabbing; }
.fsc-thumb:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.fsc-value { font-family: var(--font-mono); font-size: 11px; color: var(--text-secondary); min-width: 32px; text-align: right; }
.editor-page {
  max-width: 1050px; margin: 0 auto; background: var(--surface-1); border: 1px solid var(--divider);
  border-radius: 10px; box-shadow: var(--shadow); padding: 44px 56px 60px; min-height: 520px;
  font-family: var(--content-font); font-size: calc(16px * var(--content-font-scale)); line-height: 1.68; color: var(--text-primary);
  /* A floated wrapped image (data-wrap left/right) taller than the text next
     to it isn't counted toward this element's own height by default -- the
     page's background/border stop at the text, and the image visually spills
     out past the bottom onto the surrounding canvas. flow-root establishes a
     block formatting context so floated children ARE included in the height
     calculation, with none of overflow:hidden's side effect of clipping
     intentionally-overflowing overlays (mention menu, image handles, etc). */
  display: flow-root;
}
.editor-page:focus { outline: none; }
/* Item 5 -- Fill mode: flush with the pane's sides and the toolbar's bottom
   edge, no card chrome. Bottom padding is kept for comfortable scroll room
   past the last line -- "flush" only applies to the top/left/right per the
   request. */
[data-editor-mode="fill"] .editor-scroll { padding-top: 0; padding-left: 0; padding-right: 0; }
[data-editor-mode="fill"] .editor-page {
  max-width: none; margin: 0; border: none; border-radius: 0; box-shadow: none;
  min-height: 100%; padding: 28px 40px 60px;
}
/* Out-of-flow (position:absolute) rather than inline heading content -- an
   inline contenteditable="false" toggle sitting in the heading's own text
   could be swept up by a single extra Backspace or a select-and-retype of
   the heading (Phase 8 item 5). Pulling it into the existing 56px
   .editor-page side padding via a negative left offset removes it from the
   line-box flow contenteditable's caret/selection model walks, while it
   stays the heading's first child so it still moves with the heading with
   no position-syncing JS needed. */
.editor-page h1, .editor-page h2, .editor-page h3 { position: relative; }
.heading-collapse-toggle {
  position: absolute; left: -26px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center; width: 17px; height: 17px;
  border-radius: 4px; font-size: 13px; line-height: 1; color: var(--text-tertiary);
  cursor: pointer; user-select: none; font-family: var(--font-ui); font-weight: 400;
}
.heading-collapse-toggle:hover { background: var(--surface-3); color: var(--text-primary); }
.editor-page h1 { font-family: var(--content-font); font-size: calc(26px * var(--content-font-scale)); font-weight: 700; margin: calc(var(--h1-space-before) * 1px * var(--content-font-scale)) 0 calc(var(--h1-space-after) * 1px * var(--content-font-scale)); letter-spacing: -0.01em; }
.editor-page h2 { font-family: var(--content-font); font-size: calc(18px * var(--content-font-scale)); font-weight: 700; margin: calc(var(--h2-space-before) * 1px * var(--content-font-scale)) 0 calc(var(--h2-space-after) * 1px * var(--content-font-scale)); color: var(--text-primary); }
.editor-page h3 { font-family: var(--content-font); font-size: calc(16px * var(--content-font-scale)); font-weight: 700; margin: calc(var(--h3-space-before) * 1px * var(--content-font-scale)) 0 calc(var(--h3-space-after) * 1px * var(--content-font-scale)); color: var(--text-primary); }
/* Phase 14 -- Word-style spacing. Body paragraphs and list items take the
   user's body default (factory 0/0); a per-block override arrives as an
   inline margin written by ast.js (data-space-before/after + style), which
   beats these rules by specificity as intended. Lists, tables and figures
   carry no spacing of their own -- like a word processor, the space around
   them comes from the paragraphs beside them. */
/* Runs of spaces are content, not HTML source formatting. Without this a
   double space between sentences (or an indent typed with spaces) collapsed
   to one the moment the note was re-rendered -- the text was stored faithfully
   all along, HTML just refused to show it. pre-wrap keeps every space and
   still wraps normally; applied to the text-bearing blocks rather than the
   page so whitespace BETWEEN block tags is still ignored. */
.editor-page p, .editor-page h1, .editor-page h2, .editor-page h3,
.editor-page li, .editor-page td, .editor-page th,
.about-box p, .about-box h1, .about-box h2, .about-box h3,
.about-box li, .about-box td, .about-box th { white-space: pre-wrap; }
.editor-page p { margin: calc(var(--p-space-before) * 1px * var(--content-font-scale)) 0 calc(var(--p-space-after) * 1px * var(--content-font-scale)); }
.editor-page ul, .editor-page ol { margin: 0; padding-left: 24px; }
.editor-page li { margin: calc(var(--p-space-before) * 1px * var(--content-font-scale)) 0 calc(var(--p-space-after) * 1px * var(--content-font-scale)); }
/* A list item holding nothing but a sub-list (a parent-less nested group)
   shows no bullet of its own -- otherwise its marker lands on the first
   child's line as a double bullet. Same for a nest-only checklist item's box. */
.editor-page li.nest-only { list-style: none; }
.editor-page li.task-item.nest-only > .task-checkbox { display: none; }
.editor-page li.task-item.nest-only { padding-left: 0; }
/* Legal/outline-style nested numbering: 1. -> (a) -> (i), cycling back to
   decimal for anything deeper than three levels rather than trailing off
   undefined. Ordered lists nest as real <ol><li><ol>...</ol></li></ol> (see
   ast.js's renderListItem), so plain descendant selectors are enough --
   no per-item depth tracking needed. `decimal`'s ". " suffix is a browser
   built-in default; the two custom styles below extend the built-in
   alphabetic/roman systems purely to add the "(x) " wrapping the outline
   convention calls for. */
@counter-style lentium-paren-alpha { system: extends lower-alpha; prefix: "("; suffix: ") "; }
@counter-style lentium-paren-roman { system: extends lower-roman; prefix: "("; suffix: ") "; }
.editor-page ol { list-style-type: decimal; }
.editor-page ol ol { list-style-type: lentium-paren-alpha; }
.editor-page ol ol ol { list-style-type: lentium-paren-roman; }
.editor-page ol ol ol ol { list-style-type: decimal; }
.editor-page ul.task-list { list-style: none; padding-left: 4px; }
/* Hanging-marker layout, not flex -- flex made the checkbox and text separate
   non-wrapping row items, so any item needing more than one line (or
   containing a bold span / chip mention) broke text wrapping entirely
   (Phase 8 item 4). position:absolute pulls the checkbox out of the text
   flow so wrapped lines line up under themselves like normal paragraph text. */
.editor-page li.task-item { position: relative; padding-left: 24px; }
.editor-page li.task-item .task-checkbox { position: absolute; left: 0; top: 3px; width: 15px; height: 15px; margin: 0; accent-color: var(--primary); cursor: pointer; }
.editor-page li.task-item[data-checked="true"] { color: var(--text-tertiary); text-decoration: line-through; }
.editor-page li > ul, .editor-page li > ol { margin-top: 0; margin-bottom: 0; }
.editor-page b, .editor-page strong { font-weight: 700; }
.editor-page em { font-style: italic; }
/* Default: fill the pane. A table the user has sized carries its own inline
   width (see ast.js tableStyleAttr), which beats this by specificity. */
table.note-table { border-collapse: collapse; width: 100%; margin: 0; font-size: calc(13.5px * var(--content-font-scale)); }
table.note-table th {
  position: relative; text-align: left; background: var(--surface-2); border: 1px solid var(--border); padding: 7px 10px;
  color: var(--text-secondary); font-weight: 600; font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em;
}
table.note-table td { position: relative; border: 1px solid var(--divider); padding: 7px 10px; }
/* position: relative anchors the whole-table width grip below. */
.note-table-wrap { overflow-x: auto; margin: 0; position: relative; }
/* Whole-table width grip: a tall strip on the table's right edge, gated on
   focus like every other table control so it never clutters a note being read.
   It hangs off the table's own right edge rather than the wrap's, so it
   follows a table that's been narrowed. */
.table-width-handle {
  position: absolute; top: 0; bottom: 0; width: 7px; cursor: col-resize; visibility: hidden; z-index: 2;
  left: 0; margin-left: -3px; /* real x set from layout by positionTableWidthGrip() */
}
.note-table-wrap.table-focused .table-width-handle { visibility: visible; }
.table-width-handle::after {
  content: ""; position: absolute; inset: 3px 2px; border-radius: 2px; background: var(--border);
}
.table-width-handle:hover::after { background: var(--primary-strong); }
.tbl-ctl {
  border: none !important; background: transparent; color: var(--text-tertiary); cursor: pointer;
  text-align: center; vertical-align: middle; font-size: 12px; width: 22px; padding: 2px 4px !important;
  user-select: none;
  /* Item 21 -- visibility, not display: none, so the control row/cell keeps
     its layout space and the table doesn't jump size when focus enters/leaves. */
  visibility: hidden;
}
.note-table-wrap.table-focused .tbl-ctl { visibility: visible; }
/* Phase 8 item 3 -- column/row resize grips, same visibility-not-display
   gating as .tbl-ctl above so nothing shifts size on focus enter/leave. */
.col-resize-handle { position: absolute; top: 0; right: -3px; bottom: 0; width: 6px; cursor: col-resize; visibility: hidden; z-index: 1; }
.row-resize-handle { position: absolute; left: 0; right: 0; bottom: -3px; height: 6px; cursor: row-resize; visibility: hidden; z-index: 1; }
.note-table-wrap.table-focused .col-resize-handle,
.note-table-wrap.table-focused .row-resize-handle { visibility: visible; }
.col-resize-handle:hover, .row-resize-handle:hover { background: var(--primary-wash); }
tr[data-table-control] .tbl-ctl { padding: 3px 4px !important; }
.tbl-ctl:hover { color: var(--warning); background: var(--surface-3); }
.tbl-col-add:hover, .tbl-row-add:hover { color: var(--primary-strong); }
.tbl-row-add { width: auto; font-weight: 700; }
.inline-figure { margin: 0; position: relative; display: inline-block; max-width: 100%; }
.inline-figure img { max-width: 100%; border-radius: 7px; border: 1px solid var(--divider); display: block; }
/* Text-wrap: floats the figure so following paragraphs flow around it
   instead of stacking below (the default, data-wrap="none", keeps today's
   full-width block behavior). Capped at 45% so there's actually room left
   for text to wrap into. */
.inline-figure[data-wrap="left"] { float: left; margin: 4px 20px 12px 0; max-width: 45%; }
.inline-figure[data-wrap="right"] { float: right; margin: 4px 0 12px 20px; max-width: 45%; }
/* Centred: a block of its own with the picture in the middle and nothing
   flowing beside it. width:fit-content is what lets the auto margins centre
   an element that's otherwise sized by its content. */
.inline-figure[data-wrap="center"] {
  float: none; display: block; width: fit-content; max-width: 100%;
  margin-left: auto; margin-right: auto;
}
/* Headings read as a fresh section, not a caption for whatever's floated
   beside them -- start clear of any wrap in progress. */
.editor-page h1, .editor-page h2, .editor-page h3 { clear: both; }
.image-upload-placeholder {
  display: block; width: fit-content; padding: 8px 14px; margin: 4px 0; font-size: 12.5px; color: var(--text-tertiary);
  background: var(--surface-2); border: 1px dashed var(--border); border-radius: 8px;
}

/* ---------- image resize/crop (Phase 6) ---------- */
.crop-frame { position: relative; display: block; overflow: hidden; border-radius: 7px; border: 1px solid var(--divider); }
.crop-frame img { position: absolute; max-width: none; border: none; border-radius: 0; }
.inline-figure.image-selected img, .inline-figure.image-selected .crop-frame { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Sized/outlined to stay legible over arbitrary image content -- a plain
   var(--bg)-bordered dot could disappear entirely against a photo whose
   local color happened to match the theme background (the reported "barely
   visible" crop anchors). A white ring plus a thin dark outer ring gives the
   handle a light AND a dark edge, so at least one always contrasts against
   whatever's underneath, regardless of theme or image content. */
.img-handle {
  position: absolute; width: 13px; height: 13px; background: var(--primary); border: 2px solid #fff;
  border-radius: 50%; transform: translate(-50%, -50%); z-index: 5;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.45);
}
.img-handle:hover { background: var(--primary-strong); }
/* The crop modal's window handles (data-crop-handle) share this exact same
   8-point layout as the inline resize handles (data-handle) -- combined here
   rather than duplicated. Previously only data-handle had position rules, so
   every data-crop-handle div had no left/top at all and all 8 stacked on top
   of each other at the crop window's origin corner -- the actual cause of
   the crop anchors reading as "barely visible": there was only ever one
   handle-sized blob on screen, not eight spread around the selection. */
.img-handle[data-handle="nw"], .img-handle[data-crop-handle="nw"] { left: 0; top: 0; cursor: nwse-resize; }
.img-handle[data-handle="n"],  .img-handle[data-crop-handle="n"]  { left: 50%; top: 0; cursor: ns-resize; }
.img-handle[data-handle="ne"], .img-handle[data-crop-handle="ne"] { left: 100%; top: 0; cursor: nesw-resize; }
.img-handle[data-handle="e"],  .img-handle[data-crop-handle="e"]  { left: 100%; top: 50%; cursor: ew-resize; }
.img-handle[data-handle="se"], .img-handle[data-crop-handle="se"] { left: 100%; top: 100%; cursor: nwse-resize; }
.img-handle[data-handle="s"],  .img-handle[data-crop-handle="s"]  { left: 50%; top: 100%; cursor: ns-resize; }
.img-handle[data-handle="sw"], .img-handle[data-crop-handle="sw"] { left: 0; top: 100%; cursor: nesw-resize; }
.img-handle[data-handle="w"],  .img-handle[data-crop-handle="w"]  { left: 0; top: 50%; cursor: ew-resize; }
/* Crop-window handles live inside .crop-modal-stage, which needs its own
   overflow:hidden so the crop-window's 9999px "darken everything outside
   the crop" shadow doesn't bleed past the image into the rest of the modal.
   Centering a handle on its exact corner (the inline resize handles' -50%/
   -50% transform, fine on an unclipped figure) puts half its circle outside
   the crop window at the default full-image crop -- clipped away by that
   same overflow:hidden, which is what actually made the anchors read as
   "barely visible": most of each one was never being painted at all.
   Pulling every handle fully inward (flush against its anchor edge/corner
   instead of centered on it) keeps the whole circle inside the clipped area
   at any crop position. */
.img-handle[data-crop-handle="nw"] { transform: translate(0, 0); }
.img-handle[data-crop-handle="n"]  { transform: translate(-50%, 0); }
.img-handle[data-crop-handle="ne"] { transform: translate(-100%, 0); }
.img-handle[data-crop-handle="e"]  { transform: translate(-100%, -50%); }
.img-handle[data-crop-handle="se"] { transform: translate(-100%, -100%); }
.img-handle[data-crop-handle="s"]  { transform: translate(-50%, -100%); }
.img-handle[data-crop-handle="sw"] { transform: translate(0, -100%); }
.img-handle[data-crop-handle="w"]  { transform: translate(0, -50%); }

.tb-btn.image-ctx { display: none; }
.toolbar.has-image-selection .tb-btn.image-ctx { display: flex; }
/* Delete image sits with the other image controls but reads as the
   destructive one on hover -- same warning red the table's × controls use. */
.tb-btn.image-ctx-danger:hover { color: var(--warning); background: var(--surface-3); }

/* ---- image drag to reposition ---- */
/* The picture being dragged dims in place while its ghost travels, so the
   eye reads "this one is moving" and the drop line below is the only other
   thing that changes. */
.inline-figure.image-dragging { opacity: 0.35; }
/* Where the image will land: a purple rule at the block boundary under the
   pointer, indented to the item when it's between two list items. Lives in
   .editor-scroll (position: relative) in content coordinates so it scrolls
   with the note, like the search overlays. */
.image-drop-line {
  position: absolute; height: 2px; border-radius: 2px; background: var(--primary-strong);
  box-shadow: 0 0 0 1px var(--surface-1), 0 0 6px var(--primary-wash);
  pointer-events: none; z-index: 6;
}
.image-drop-line::before, .image-drop-line::after {
  content: ""; position: absolute; top: 50%; width: 8px; height: 8px; border-radius: 50%;
  background: var(--primary-strong); transform: translateY(-50%);
}
.image-drop-line::before { left: -4px; }
.image-drop-line::after { right: -4px; }

/* crop mode modal */
.crop-modal-overlay {
  position: fixed; inset: 0; background: rgba(6, 8, 11, 0.7); z-index: 100; display: none;
  align-items: center; justify-content: center; padding: 24px;
}
.crop-modal-overlay.open { display: flex; }
.crop-modal-panel { background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); padding: 20px; max-width: 90vw; max-height: 90vh; display: flex; flex-direction: column; gap: 14px; }
.crop-modal-title { font-size: 14px; font-weight: 700; color: var(--text-primary); }
.crop-modal-stage { position: relative; display: inline-block; user-select: none; overflow: hidden; border-radius: 6px; }
.crop-modal-stage img { display: block; max-width: min(70vw, 800px); max-height: 60vh; border-radius: 0; }
.crop-window { position: absolute; outline: 2px solid var(--primary); box-shadow: 0 0 0 9999px rgba(6, 8, 11, 0.65); cursor: move; }
.crop-modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
.crop-modal-actions .btn { width: auto; padding: 8px 18px; }

/* ---------- status bar ---------- */
.status-bar {
  grid-area: status; background: var(--primary); color: #0B0B0F; display: flex; align-items: center;
  padding: 0 12px; font-size: 11px; gap: 16px; font-weight: 600;
}
.status-bar .sb-item { display: flex; align-items: center; gap: 5px; opacity: 0.92; }
.status-bar .sb-spacer { flex: 1; }
.status-bar button.sb-item { background: transparent; border: none; color: inherit; font: inherit; cursor: pointer; padding: 0; }
.status-bar button.sb-item:hover { opacity: 1; text-decoration: underline; }

/* ---------- Timeline strip (Phase 11) ----------
   Sits in .main's normal flex flow directly above the status bar, so opening
   it shortens the editor rather than covering it -- scrubbing history while
   the content you're comparing against is hidden would defeat the point. */
.timeline-strip {
  flex: none; background: var(--surface-2); border-top: 1px solid var(--border);
  padding: 7px 14px 9px; display: flex; flex-direction: column; gap: 7px;
}
.timeline-strip[hidden] { display: none; }
.timeline-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.timeline-label { font-size: 11.5px; color: var(--text-secondary); font-weight: 600; }
.timeline-label strong { color: var(--text-primary); font-weight: 600; }
.timeline-actions { display: flex; align-items: center; gap: 8px; }
.timeline-btn {
  border: 1px solid var(--primary); background: var(--primary-wash); color: var(--primary-strong);
  border-radius: 999px; padding: 3px 12px; font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer;
}
.timeline-btn:hover:not(:disabled) { background: var(--primary-strong); color: var(--bg); border-color: var(--primary-strong); }
.timeline-btn:disabled { opacity: 0.4; cursor: default; border-color: var(--border); background: transparent; color: var(--text-tertiary); }
.timeline-close { border: none; background: transparent; color: var(--text-tertiary); cursor: pointer; display: flex; padding: 2px; }
.timeline-close svg { width: 13px; height: 13px; }
.timeline-close:hover { color: var(--text-primary); }

/* Ticks are evenly spaced by index rather than by timestamp: history is
   thinned with age, so time-proportional spacing would crush a week of work
   into a sliver at one end and stretch one afternoon across the rest. */
.timeline-track {
  position: relative; display: flex; align-items: center; gap: 0;
  height: 22px; cursor: pointer; outline: none;
}
.timeline-track::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 2px;
  transform: translateY(-50%); background: var(--border); border-radius: 2px;
}
.timeline-track:focus-visible::before { background: var(--primary); }
.timeline-tick {
  position: relative; flex: 1; height: 100%; border: none; background: transparent;
  padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.timeline-tick::after {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--surface-3); border: 1.5px solid var(--text-tertiary); transition: transform 0.12s, background 0.12s;
}
.timeline-tick:hover::after { transform: scale(1.25); border-color: var(--primary); }
.timeline-tick.active::after { background: var(--primary-strong); border-color: var(--primary-strong); transform: scale(1.35); }
/* "Now" is the live document, not a stored snapshot -- distinct shape so it
   never reads as just another history point. */
.timeline-tick.timeline-now::after { border-radius: 3px; width: 10px; height: 10px; border-color: var(--primary); }
.timeline-tick.timeline-now.active::after { background: var(--primary-strong); }

.timeline-btn-ghost { border-color: var(--border); background: transparent; color: var(--text-secondary); }
.timeline-btn-ghost:hover:not(:disabled) { background: var(--surface-3); color: var(--text-primary); border-color: var(--text-tertiary); }
.timeline-btn-ghost.active { border-color: var(--primary); background: var(--primary-wash); color: var(--primary-strong); }
/* Miniature of the real redline styles, so the legend reads as a sample of
   what's on screen rather than a colour key you have to decode. */
.tl-legend { display: inline-flex; gap: 8px; }
.tl-legend ins, .tl-legend del { font-size: 11px; }

/* ---------- Redline (Phase 11) ----------
   Blue underline for additions, red strikethrough for deletions -- the
   convention from tracked-changes documents. Deliberately NOT the brand
   purple: this is transient annotation on someone else's past writing, and
   borrowing the accent would make it compete with real UI affordances. */
:root {
  --diff-add: #5B93F5;
  --diff-add-wash: rgba(91, 147, 245, 0.13);
  --diff-del: #E5674B;
  --diff-del-wash: rgba(229, 103, 75, 0.13);
}
:root[data-theme="light"] {
  --diff-add: #2C5FBF;
  --diff-add-wash: rgba(44, 95, 191, 0.10);
  --diff-del: #B23A20;
  --diff-del-wash: rgba(178, 58, 32, 0.10);
}

.editor-page ins.diff-add,
.editor-page .diff-add {
  color: var(--diff-add); background: var(--diff-add-wash);
  text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 2px;
}
.editor-page del.diff-del,
.editor-page .diff-del {
  color: var(--diff-del); background: var(--diff-del-wash);
  text-decoration: line-through; text-decoration-thickness: 1.5px;
}
/* Block-level marks wrap whole paragraphs/headings; make them read as a
   changed region with a margin bar rather than one enormous underline. */
.editor-page ins.diff-add > *,
.editor-page del.diff-del > * { margin-left: 0; }
.editor-page ins.diff-add { display: block; border-left: 2px solid var(--diff-add); padding-left: 10px; text-decoration: none; }
.editor-page del.diff-del { display: block; border-left: 2px solid var(--diff-del); padding-left: 10px; text-decoration: none; }
.editor-page del.diff-del > * { text-decoration: line-through; }
/* ...but an inline <ins>/<del> inside a paragraph stays inline. */
.editor-page p ins.diff-add, .editor-page h1 ins.diff-add, .editor-page h2 ins.diff-add, .editor-page h3 ins.diff-add,
.editor-page li ins.diff-add, .editor-page td ins.diff-add, .editor-page th ins.diff-add {
  display: inline; border-left: none; padding-left: 0; text-decoration: underline;
}
.editor-page p del.diff-del, .editor-page h1 del.diff-del, .editor-page h2 del.diff-del, .editor-page h3 del.diff-del,
.editor-page li del.diff-del, .editor-page td del.diff-del, .editor-page th del.diff-del {
  display: inline; border-left: none; padding-left: 0; text-decoration: line-through;
}
/* Chips keep their own identity but pick up the change colour on their border. */
.editor-page ins.diff-add .chip { border-color: var(--diff-add); }
.editor-page del.diff-del .chip { border-color: var(--diff-del); opacity: 0.75; }
/* List items and table rows/cells carry the marker as a class (a <ul> may only
   contain <li>, a <tr> only cells), so they need their own rules. */
.editor-page li.diff-add { color: var(--diff-add); background: var(--diff-add-wash); text-decoration: underline; text-underline-offset: 2px; }
.editor-page li.diff-del { color: var(--diff-del); background: var(--diff-del-wash); text-decoration: line-through; }
.editor-page tr.diff-add > *, .editor-page td.diff-add, .editor-page th.diff-add { color: var(--diff-add); background: var(--diff-add-wash); }
.editor-page tr.diff-del > *, .editor-page td.diff-del, .editor-page th.diff-del { color: var(--diff-del); background: var(--diff-del-wash); text-decoration: line-through; }

/* ---------- Live co-editing (Phase 12) ----------
   Remote caret flags live in #editorScroll content coordinates (the search
   overlays' technique), one hue per client hashed from its id. The flash on
   remotely-changed blocks reuses the primary wash so it reads as "the other
   device did this", distinct from the green save flash. */
.remote-caret {
  position: absolute; width: 2px; pointer-events: none; z-index: 15;
  background: hsl(var(--caret-hue), 70%, 60%);
}
.remote-caret-label {
  position: absolute; top: -14px; left: -2px; white-space: nowrap;
  background: hsl(var(--caret-hue), 70%, 45%); color: #fff;
  font-size: 9.5px; font-weight: 700; line-height: 1; padding: 2px 5px; border-radius: 3px 3px 3px 0;
  font-family: var(--font-ui);
}
:root[data-theme="light"] .remote-caret { background: hsl(var(--caret-hue), 65%, 45%); }
:root[data-theme="light"] .remote-caret-label { background: hsl(var(--caret-hue), 65%, 38%); }

.remote-change-flash { animation: remoteChangePulse 1.4s ease; }
@keyframes remoteChangePulse {
  0% { background: var(--primary-wash); }
  100% { background: transparent; }
}

/* Phase 14 -- word-level cues for another device's edit. The block is shown
   as a purple redline for a moment (see patchEditorFromBody): insertions get
   a straight purple underline that fades out once the deletions have gone;
   deletions flash struck-through twice, then vanish. Overrides the
   timeline's blue/red redline colours inside the transition only. */
.editor-page .remote-transition ins.diff-add,
.editor-page .remote-transition .diff-add {
  color: inherit; background: transparent;
  text-decoration: underline; text-decoration-color: var(--primary-strong);
  text-decoration-thickness: 2px; text-underline-offset: 3px;
  transition: text-decoration-color 0.9s ease;
}
.editor-page .remote-transition li.diff-add, .editor-page .remote-transition tr.diff-add > *,
.editor-page .remote-transition td.diff-add, .editor-page .remote-transition th.diff-add {
  color: inherit; background: transparent;
  text-decoration: underline; text-decoration-color: var(--primary-strong); text-decoration-thickness: 2px;
  transition: text-decoration-color 0.9s ease;
}
.editor-page .remote-transition.remote-fading ins.diff-add,
.editor-page .remote-transition.remote-fading .diff-add,
.editor-page .remote-transition.remote-fading li.diff-add,
.editor-page .remote-transition.remote-fading td.diff-add,
.editor-page .remote-transition.remote-fading th.diff-add { text-decoration-color: transparent; }
.editor-page .remote-transition del.diff-del,
.editor-page .remote-transition .diff-del,
.editor-page .remote-transition li.diff-del,
.editor-page .remote-transition tr.diff-del > *,
.editor-page .remote-transition td.diff-del,
.editor-page .remote-transition th.diff-del {
  color: inherit; background: transparent;
  text-decoration: line-through; text-decoration-color: var(--primary-strong); text-decoration-thickness: 2px;
  animation: remoteDelFlash 0.35s ease-in-out 2;
}
.editor-page .remote-transition ins.diff-add .chip { border-color: var(--primary-strong); }
.editor-page .remote-transition del.diff-del .chip { border-color: var(--primary-strong); opacity: 0.6; }
@keyframes remoteDelFlash {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}
/* A whole block another device added: underline everything, fade out. */
.editor-page .remote-new {
  text-decoration: underline; text-decoration-color: var(--primary-strong); text-decoration-thickness: 2px; text-underline-offset: 3px;
  animation: remoteNewFade 1.6s ease forwards;
}
@keyframes remoteNewFade {
  0%, 45% { text-decoration-color: var(--primary-strong); }
  100% { text-decoration-color: transparent; }
}
/* A block another device deleted: struck through, two flashes, gone. */
.editor-page .remote-removed {
  text-decoration: line-through; text-decoration-color: var(--primary-strong); text-decoration-thickness: 2px;
  animation: remoteDelFlash 0.35s ease-in-out 2;
}
@media (prefers-reduced-motion: reduce) {
  .editor-page .remote-transition del.diff-del, .editor-page .remote-transition .diff-del,
  .editor-page .remote-removed, .editor-page .remote-new { animation: none; }
}

.live-pill {
  display: inline-flex; align-items: center; white-space: nowrap;
  background: var(--primary-wash); color: var(--primary-strong);
  border: 1px solid var(--primary); border-radius: 999px;
  padding: 2px 10px; font-size: 11px; font-weight: 600;
}

/* Phase 14 -- save state in words, in the breadcrumb bar. Same colour
   language as the tab dot, plus a leading dot so the state reads at a glance
   even before the words do. */
.save-state {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  background: none; border: 1px solid transparent; border-radius: 999px;
  padding: 2px 9px 2px 7px; font-size: 11px; font-family: inherit; color: var(--text-tertiary);
  cursor: pointer; transition: color .15s, border-color .15s, background-color .15s;
}
.save-state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--success); flex: none; transition: background-color .15s; }
.save-state[data-state="unsaved"] { color: var(--text-secondary); }
.save-state[data-state="unsaved"]::before { background: var(--highlight); }
.save-state[data-state="saving"]::before { background: var(--primary-strong); animation: pulseDot 1s ease-in-out infinite; }
.save-state[data-state="offline"]::before { background: var(--text-tertiary); }
.save-state[data-state="error"] { color: var(--warning); border-color: var(--warning); }
.save-state[data-state="error"]::before { background: var(--warning); }
.save-state:not(:disabled):hover { color: var(--text-primary); border-color: var(--border); background: var(--surface-2); }
.save-state:disabled { cursor: default; }

/* ---------- Spellcheck (Phase 13) ----------
   Painted through the CSS Custom Highlight API, so there is no element to
   style -- only the ::highlight() pseudo. Red wavy underline: the universal
   spelling convention, deliberately distinct from Scan-for-Links' purple
   wavy (a suggestion to tag) and the redline's straight underline (a diff). */
::highlight(lentium-spell) {
  text-decoration: underline wavy #E5534B;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
}
:root[data-theme="light"] ::highlight(lentium-spell) { text-decoration-color: #C8382F; }

/* One row per language: radio, flag, name, and the spelling sample pushed to
   the right so the three samples line up as a column of their own. */
.spell-lang-options { display: flex; flex-direction: column; gap: 2px; }
.spell-lang-option {
  display: flex; align-items: center; gap: 9px; padding: 7px 9px; border-radius: 7px;
  font-size: 13.5px; color: var(--text-primary); cursor: pointer;
  border: 1px solid transparent;
}
.spell-lang-option:hover { background: var(--surface-3); }
.spell-lang-option:has(input:checked) { background: var(--primary-wash); border-color: var(--primary); }
.spell-lang-option input { accent-color: var(--primary); margin: 0; flex: none; }
/* Inline SVG, not an emoji flag: Windows ships no country-flag glyphs, so
   U+1F1FA U+1F1F8 renders there as the bare letters "US" in a box. */
.spell-flag { flex: none; width: 20px; height: 14px; border-radius: 2px; display: block; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); }
/* The name gives way first if the dialog is ever narrower than the row wants;
   the sample never wraps, so the three samples always line up as one column. */
.spell-lang-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.spell-lang-sample { margin-left: auto; padding-left: 10px; flex: none; white-space: nowrap; font-size: 12px; color: var(--text-tertiary); font-style: italic; }
.spell-lang-option:has(input:checked) .spell-lang-sample { color: var(--text-secondary); }
.spell-dict-count { color: var(--text-tertiary); font-weight: 400; }
.spell-dict-list {
  display: flex; flex-wrap: wrap; gap: 6px; max-height: 180px; overflow-y: auto;
  padding: 8px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
}
.spell-dict-word {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 4px 2px 9px;
  background: var(--surface-3); border: 1px solid var(--border); border-radius: 999px; font-size: 12.5px;
}
.spell-dict-remove {
  border: none; background: transparent; color: var(--text-tertiary); cursor: pointer;
  font-size: 14px; line-height: 1; padding: 0 4px; border-radius: 50%;
}
.spell-dict-remove:hover { color: var(--warning); background: rgba(245, 107, 74, 0.12); }
.spell-dict-empty { font-size: 12.5px; color: var(--text-tertiary); }

/* ---------- Scan for links: tag suggestions ----------
   A wavy purple underline says "this could be a tag" without changing the
   text -- deliberately the same visual language as a spellcheck squiggle,
   since it's the same idea: a suggestion you may accept or ignore. */
.crumb-scan-btn {
  display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--border);
  background: transparent; color: var(--text-secondary); border-radius: 999px;
  padding: 3px 10px; font: inherit; font-size: 11.5px; cursor: pointer; white-space: nowrap;
}
.crumb-scan-btn svg { width: 13px; height: 13px; }
.crumb-scan-btn:hover { border-color: var(--primary); color: var(--primary-strong); background: var(--primary-wash); }

.editor-page .link-suggestion {
  text-decoration: underline wavy var(--primary);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  cursor: context-menu;
}
.editor-page .link-suggestion:hover { background: var(--primary-wash); border-radius: 3px; }

/* Read-only scrubbing state on the editor itself. */
.editor-page.timeline-viewing { outline: 2px solid var(--primary); outline-offset: 4px; opacity: 0.94; }
.timeline-viewing-badge {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 20;
  background: var(--primary); color: #0B0B0F; border-radius: 999px; padding: 4px 14px;
  font-size: 11.5px; font-weight: 700; box-shadow: var(--shadow); pointer-events: none; white-space: nowrap;
}

/* ---------- AST debug modal ---------- */
.ast-overlay {
  position: fixed; inset: 0; background: rgba(6, 8, 11, 0.55); z-index: 80; display: none;
  align-items: flex-end; justify-content: center;
}
.ast-overlay.open { display: flex; }
.ast-panel {
  width: min(760px, 94vw); max-height: 70vh; background: var(--surface-1); border: 1px solid var(--border);
  border-bottom: none; border-radius: 12px 12px 0 0; box-shadow: var(--shadow); display: flex; flex-direction: column; overflow: hidden;
}
.ast-header { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--border); font-size: 12.5px; color: var(--text-secondary); }
.ast-header strong { color: var(--text-primary); font-weight: 700; }
.ast-header .close-ast { margin-left: auto; background: transparent; border: none; color: var(--text-tertiary); cursor: pointer; width: 24px; height: 24px; border-radius: 5px; display: flex; align-items: center; justify-content: center; }
.ast-header .close-ast:hover { background: var(--surface-3); color: var(--text-primary); }
.ast-body { overflow: auto; padding: 14px 16px; margin: 0; font-family: var(--font-mono); font-size: 12px; line-height: 1.6; color: var(--text-secondary); }
.ast-body .k { color: var(--primary-strong); }
.ast-body .s { color: var(--tag); }

/* ---------- generic in-page dialog (replaces window.prompt/confirm) ---------- */
.dialog-overlay { position: fixed; inset: 0; background: rgba(6, 8, 11, 0.55); z-index: 95; display: none; align-items: center; justify-content: center; padding: 24px; }
.dialog-overlay.open { display: flex; }
.dialog-panel { width: min(420px, 100%); background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); padding: 22px; }
.dialog-title { font-size: 15px; font-weight: 700; color: var(--text-primary); margin: 0 0 6px; }
.dialog-message { font-size: 13.5px; color: var(--text-secondary); margin: 0 0 18px; line-height: 1.5; }
.dialog-field { margin-bottom: 14px; }
.dialog-field:last-of-type { margin-bottom: 0; }
.dialog-field > label { display: block; font-size: 12.5px; color: var(--text-secondary); margin-bottom: 6px; }
/* Text-entry fields only. Without the :not() guard this also hit radios and
   checkboxes -- a radio stretched to width:100% with 9px padding renders as a
   lone dot floating in a full-width box, which is exactly what made the
   Spelling dialog's language options look broken (dot on one line, its label
   pushed onto the next). */
.dialog-field input:not([type="radio"]):not([type="checkbox"]) {
  width: 100%; background: var(--surface-3); border: 1px solid var(--border); border-radius: 7px;
  padding: 9px 11px; font-size: 14px; color: var(--text-primary); font-family: inherit;
}
.dialog-field input:not([type="radio"]):not([type="checkbox"]):focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-wash); }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.dialog-actions .btn { width: auto; padding: 8px 18px; }
.dialog-actions .btn-danger { background: var(--warning); color: #0B0B0F; }
.dialog-actions .btn-danger:hover { filter: brightness(1.1); }
.dialog-actions [data-dlg="reset"] { margin-right: auto; }

.ref-list { list-style: none; margin: 0 0 4px; padding: 0; max-height: 160px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; }
.ref-list li { padding: 7px 10px; font-size: 13px; color: var(--text-primary); border-bottom: 1px solid var(--divider); }
.ref-list li:last-child { border-bottom: none; }
.ref-list-bc { color: var(--text-tertiary); font-size: 11.5px; }

.heading-spacing-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.heading-spacing-row label { margin: 0; font-weight: 600; color: var(--text-primary); flex: none; width: 26px; }
.heading-spacing-inputs { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-tertiary); }
.heading-spacing-inputs input {
  width: 56px; background: var(--surface-3); border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 8px; font-size: 13px; color: var(--text-primary); font-family: inherit; text-align: right;
}
.heading-spacing-inputs input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-wash); }

/* ============================================================
   Phase 3: chip colors, outline panel, entity/flag/date auto-pages,
   mention menu + date picker, global search dialog.
   Chip-color mapping (per the approved plan): >>note -> Link purple,
   @entity -> Tag blue, --marker -> desaturated Success green, //flag and
   **date pull two more hues from the iridescent family since the mood
   board's six semantic slots don't map 1:1 onto five chip kinds.
   ============================================================ */
:root {
  --chip-entity: var(--tag);
  --chip-entity-wash: rgba(91, 147, 245, 0.14);
  --chip-entity-border: rgba(91, 147, 245, 0.35);

  --chip-flag: #E17E9E;
  --chip-flag-wash: rgba(225, 126, 158, 0.14);
  --chip-flag-border: rgba(225, 126, 158, 0.35);

  --chip-date: #7C93B0;
  --chip-date-wash: rgba(124, 147, 176, 0.14);
  --chip-date-border: rgba(124, 147, 176, 0.35);

  --chip-noteref: var(--link);
  --chip-noteref-wash: var(--primary-wash);
  --chip-noteref-border: rgba(139, 124, 246, 0.35);

  --chip-marker: #6FAE7C;
  --chip-marker-wash: rgba(111, 174, 124, 0.14);
  --chip-marker-border: rgba(111, 174, 124, 0.4);

  --chip-attachment: #C99A4D;
  --chip-attachment-wash: rgba(201, 154, 77, 0.14);
  --chip-attachment-border: rgba(201, 154, 77, 0.35);
}

/* chip-entity/chip-noteref aren't repeated here -- they're var(--tag)/
   var(--link) above, so they already pick up the light overrides on those. */
:root[data-theme="light"] {
  --chip-flag: #B03D68;
  --chip-flag-wash: rgba(176, 61, 104, 0.10);
  --chip-flag-border: rgba(176, 61, 104, 0.3);

  --chip-date: #4F6483;
  --chip-date-wash: rgba(79, 100, 131, 0.10);
  --chip-date-border: rgba(79, 100, 131, 0.3);

  --chip-marker: #3F8A50;
  --chip-marker-wash: rgba(63, 138, 80, 0.10);
  --chip-marker-border: rgba(63, 138, 80, 0.35);

  --chip-attachment: #8A6423;
  --chip-attachment-wash: rgba(138, 100, 35, 0.10);
  --chip-attachment-border: rgba(138, 100, 35, 0.3);
}

/* ---------- chips ---------- */
.chip {
  display: inline-flex; align-items: center; gap: 4px; font-weight: 600; font-size: 13.5px;
  padding: 1px 8px 1px 6px; border-radius: 5px; cursor: pointer; line-height: 1.5; vertical-align: baseline;
}
.chip svg { width: 12px; height: 12px; flex: none; }
.chip-entity { background: var(--chip-entity-wash); color: var(--chip-entity); border: 1px solid var(--chip-entity-border); }
.chip-flag { background: var(--chip-flag-wash); color: var(--chip-flag); border: 1px solid var(--chip-flag-border); font-family: var(--font-mono); font-weight: 600; font-size: 12.5px; }
.chip-date { background: var(--chip-date-wash); color: var(--chip-date); border: 1px solid var(--chip-date-border); }
.chip-noteref { background: var(--chip-noteref-wash); color: var(--chip-noteref); border: 1px solid var(--chip-noteref-border); }
.chip-marker { background: var(--chip-marker-wash); color: var(--chip-marker); border: 1px dashed var(--chip-marker-border); cursor: default; }
.chip-attachment { background: var(--chip-attachment-wash); color: var(--chip-attachment); border: 1px solid var(--chip-attachment-border); }
.chip-attachment.chip-uploading { cursor: default; opacity: 0.7; font-style: italic; }
.chip:hover:not(.chip-marker):not(.chip-uploading) { filter: brightness(1.15); text-decoration: underline; }

/* ---------- range tags (Phase 17) ----------
   Tint (background) + one stacked bottom-inset line per tag on the run,
   both set inline per-element by ast.js's wrapTagRange() since the color
   set and count vary per run -- CSS here only supplies structure/hover, not
   color. Deliberately distinct from .highlight's flat background: a tag
   always carries at least the outline, a highlight never does. */
.rtag { border-radius: 3px; padding: 0 1px; cursor: pointer; }
.rtag:hover { filter: brightness(1.15); }
.rtag.rtag-active { filter: brightness(1.3); }

/* ---------- outline panel (4th column) ---------- */
.app.has-outline { grid-template-columns: 44px 240px 1fr 250px; grid-template-areas: "activity side main outline" "activity side status outline"; }
.outline { grid-area: outline; background: var(--surface-1); border-left: 1px solid var(--border); min-width: 0; position: relative; overflow: visible; transition: border-color 0.15s; }
.outline.collapsed { border-color: transparent; }
/* split top/bottom, mirroring .side-inner/.side-notes/.side-resize-handle/.side-ref-pane below */
.outline-inner { height: 100%; display: flex; flex-direction: column; overflow: hidden; }
.outline-header { padding: 11px 14px 7px; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-tertiary); flex: none; }
.outline-body { flex: 1 1 55%; overflow-y: auto; padding: 2px 6px 16px; min-height: 100px; }
.outline-empty { padding: 16px 12px; font-size: 12px; line-height: 1.6; color: var(--text-tertiary); }
.outline-heading { display: flex; align-items: baseline; gap: 7px; padding: 6px 8px; border-radius: 5px; cursor: pointer; font-size: 12.5px; color: var(--text-primary); font-weight: 600; margin-top: 2px; }
.outline-heading:hover { background: var(--surface-3); }
.outline-heading .oh-tag { flex: none; font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; color: var(--text-tertiary); border: 1px solid var(--border); border-radius: 3px; padding: 0 3px; line-height: 1.5; }
.outline-heading .oh-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.outline-heading.outline-current { box-shadow: inset 0 0 0 1px var(--primary); }
.outline-h2 { padding-left: 20px; font-weight: 500; font-size: 12px; }
.outline-h3 { padding-left: 32px; font-weight: 400; font-size: 11.5px; color: var(--text-secondary); }
.outline-chips { padding-left: 20px; margin-bottom: 2px; }
.outline-h2-chips { padding-left: 32px; }
.outline-h3-chips { padding-left: 44px; }
.outline-chip-row { display: flex; align-items: center; gap: 6px; padding: 3.5px 8px; border-radius: 5px; cursor: pointer; font-size: 12px; }
.outline-chip-row:hover { background: var(--surface-3); }
.outline-chip-row .oc-dot { width: 14px; height: 14px; border-radius: 3px; flex: none; display: flex; align-items: center; justify-content: center; }
.outline-chip-row .oc-dot svg { width: 9px; height: 9px; }
.outline-chip-row.entity { color: var(--chip-entity); }
.outline-chip-row.entity .oc-dot { background: var(--chip-entity-wash); border: 1px solid var(--chip-entity-border); }
.outline-chip-row.flag { color: var(--chip-flag); }
.outline-chip-row.flag .oc-dot { background: var(--chip-flag-wash); border: 1px solid var(--chip-flag-border); font-family: var(--font-mono); font-size: 8px; font-weight: 700; }
.outline-chip-row.date { color: var(--chip-date); }
.outline-chip-row.date .oc-dot { background: var(--chip-date-wash); border: 1px solid var(--chip-date-border); }
.outline-chip-row.noteref { color: var(--chip-noteref); }
.outline-chip-row.noteref .oc-dot { background: var(--chip-noteref-wash); border: 1px solid var(--chip-noteref-border); }
.outline-chip-row.marker { color: var(--chip-marker); }
.outline-chip-row.marker .oc-dot { background: var(--chip-marker-wash); border: 1px dashed var(--chip-marker-border); }
.outline-chip-row .oc-label { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.outline-chip-row:hover .oc-label { color: var(--text-primary); }

/* ---------- Lens View (outline pane's bottom split) ---------- */
/* Item 3: #lensPane's home wrapper -- display:contents so #lensPane keeps
   sizing itself directly against .outline-inner's flex column exactly as
   before, whether or not it's currently the node hosting #lensPane. */
.outline-lens-mount { display: contents; }
.lens-pane { flex: 1 1 45%; min-height: 120px; display: flex; flex-direction: column; }
.lens-pane-header {
  padding: 8px 8px 6px 14px; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary); flex: none; display: flex; align-items: center; justify-content: space-between;
}
.lens-pane-header-actions { display: flex; align-items: center; gap: 2px; flex: none; }
.lens-fullscreen-btn {
  flex: none; width: 20px; height: 20px; border-radius: 5px; border: none; background: transparent;
  color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.lens-fullscreen-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.lens-fullscreen-btn svg { width: 13px; height: 13px; transition: transform 0.12s; }
/* Collapsed: only the header (title + the two buttons, so it can be
   un-collapsed) stays -- the graph/empty-state hides, and the pane stops
   claiming a flex share so the note outline above it fills the pane. */
.lens-pane.collapsed { flex: none; }
.lens-pane.collapsed .lens-canvas-wrap,
.lens-pane.collapsed .lens-empty { display: none; }
.lens-pane.collapsed #lensCollapseBtn svg { transform: rotate(-90deg); }
.lens-canvas-wrap { flex: 1; min-height: 0; position: relative; }
.lens-canvas-wrap canvas { display: block; width: 100%; height: 100%; cursor: default; }
.lens-tooltip {
  position: absolute; pointer-events: none; font-size: 12px;
  background: var(--surface-3); border: 1px solid var(--border); border-radius: 7px;
  padding: 6px 10px; color: var(--text-primary); box-shadow: var(--shadow);
  transform: translate(-50%, -130%); white-space: nowrap; opacity: 0; transition: opacity .12s ease; z-index: 3;
}
.lens-tooltip .lt-kind { color: var(--text-tertiary); font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; margin-left: 6px; }

/* ---------- Lens View fullscreen (whole-workspace graph) ---------- */
.lens-fullscreen-overlay { position: fixed; inset: 0; z-index: 92; background: rgba(6, 8, 11, 0.65); display: none; align-items: center; justify-content: center; padding: 4vh 4vw; }
.lens-fullscreen-overlay.open { display: flex; }
.lens-fullscreen-panel {
  width: 100%; height: 100%; max-width: 1600px; background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: var(--shadow); display: flex; flex-direction: column; overflow: hidden;
}
.lens-fullscreen-header {
  flex: none; display: flex; align-items: center; justify-content: space-between; padding: 12px 16px;
  border-bottom: 1px solid var(--divider); font-size: 13px; font-weight: 600; color: var(--text-primary);
}
.lens-fullscreen-canvas-wrap { flex: 1; min-height: 0; position: relative; background: var(--bg); }
.lens-fullscreen-canvas-wrap canvas { display: block; width: 100%; height: 100%; cursor: default; }
.lens-empty { padding: 16px 12px; font-size: 12px; line-height: 1.6; color: var(--text-tertiary); flex: 1; }

/* ---------- entity/flag/date auto-pages ---------- */
.auto-page-badge { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 3px 9px; border-radius: 20px; margin-bottom: 14px; }
.badge-entity { background: var(--chip-entity-wash); color: var(--chip-entity); border: 1px solid var(--chip-entity-border); }
.badge-flag { background: var(--chip-flag-wash); color: var(--chip-flag); border: 1px solid var(--chip-flag-border); }
.badge-date { background: var(--chip-date-wash); color: var(--chip-date); border: 1px solid var(--chip-date-border); }
/* Item 4 -- description and reference list side by side, not stacked.
   Stacks back to a single column once the pane is too narrow for two
   genuinely usable columns (the app has no other responsive breakpoints,
   but this one component regularly gets viewed at outline/side-panel-heavy
   widths where 50/50 would squeeze the description unreadably thin). */
.about-layout { display: grid; grid-template-columns: 1.3fr 1fr; gap: 28px; align-items: start; }
@media (max-width: 640px) {
  .about-layout { grid-template-columns: 1fr; }
}
.about-layout-refs { min-width: 0; }
.about-box { font-family: var(--content-font); font-size: calc(15px * var(--content-font-scale)); line-height: 1.6; color: var(--text-primary); padding: 12px 14px; background: var(--surface-2); border: 1px dashed var(--border); border-radius: 8px; min-height: 120px; }
.about-box:focus { outline: none; border-style: solid; border-color: var(--primary); }
.about-box[data-empty="true"]:before { content: attr(data-placeholder); color: var(--text-tertiary); }
.backlink-section-title { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-tertiary); margin: 0 0 10px; }
.backlink-item { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border: 1px solid var(--divider); border-radius: 8px; margin-bottom: 8px; cursor: pointer; }
.backlink-item:hover { border-color: var(--primary); background: var(--primary-wash); }
.backlink-item .bl-head { display: flex; align-items: baseline; gap: 6px; font-size: 11.5px; color: var(--text-tertiary); }
.backlink-item .bl-crumb { flex: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 140px; }
.backlink-item .bl-crumb:after { content: "/"; margin-left: 6px; color: var(--text-tertiary); }
.backlink-item .bl-source { color: var(--text-primary); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.backlink-item .bl-time { margin-left: auto; flex: none; font-family: var(--font-mono); }
.backlink-item .bl-snippet { font-size: 13.5px; color: var(--text-primary); line-height: 1.5; }
.empty-backlinks { font-size: 13px; color: var(--text-tertiary); font-style: italic; }

/* Phase 17 -- range-tag "Tagged" backlinks: same shape as .backlink-item,
   plus a colored left edge (the tagged item's own chip color, set inline by
   app.js) and a "Tagged" badge in place of the timestamp, and full
   multi-line tagged text instead of a single-line snippet. */
.tag-range-backlink-item { border-left-width: 3px; }
.tag-range-backlink-item .bl-tagged-badge { margin-left: auto; flex: none; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-tertiary); }
.tag-range-backlink-item .bl-tag-text { font-size: 13.5px; color: var(--text-primary); line-height: 1.5; }
.tag-range-backlink-item .bl-tag-line + .bl-tag-line { margin-top: 4px; padding-top: 4px; border-top: 1px dashed var(--divider); }

/* ---------- mention dropdown (@ // ** >> --) ---------- */
.mention-menu { position: fixed; z-index: 60; min-width: 220px; max-width: 280px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; box-shadow: var(--shadow); padding: 5px; display: none; }
.mention-menu.open { display: block; }
.mention-item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 13px; }
.mention-item .mi-icon { width: 20px; height: 20px; border-radius: 5px; flex: none; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; }
.mention-item .mi-icon svg { width: 11px; height: 11px; }
.mention-item.entity .mi-icon { background: var(--chip-entity-wash); color: var(--chip-entity); border: 1px solid var(--chip-entity-border); }
.mention-item.flag .mi-icon { background: var(--chip-flag-wash); color: var(--chip-flag); border: 1px solid var(--chip-flag-border); font-family: var(--font-mono); }
.mention-item.date .mi-icon { background: var(--chip-date-wash); color: var(--chip-date); border: 1px solid var(--chip-date-border); }
.mention-item.noteref .mi-icon, .mention-item.wholenote .mi-icon { background: var(--chip-noteref-wash); color: var(--chip-noteref); border: 1px solid var(--chip-noteref-border); }
.mention-item.marker .mi-icon { background: var(--chip-marker-wash); color: var(--chip-marker); border: 1px dashed var(--chip-marker-border); }
.mention-item.heading .mi-icon { background: var(--bg); color: var(--text-secondary); border: 1px solid var(--border); font-family: var(--font-mono); font-size: 9px; }
.mention-item .mi-sub { margin-left: auto; font-size: 10.5px; color: var(--text-tertiary); }
.mention-item.selected { background: var(--primary-wash); }
.mention-item .mi-new { color: var(--text-secondary); font-style: italic; }
.mention-empty { padding: 10px 8px; font-size: 12px; color: var(--text-tertiary); }

/* ---------- range tags (Phase 17): entry popup + view/remove popup ----------
   Same visual shell as .mention-menu (surface-2, border, shadow) since they
   read as the same family of floating popup, but kept as separate elements
   -- see app.js's RANGE TAGS section for why. */
.tb-btn-tag { width: auto; padding: 0 10px; gap: 6px; font-size: 12.5px; font-weight: 600; }
.tb-btn-tag svg { width: 14px; height: 14px; }
.tb-btn-tag:disabled { opacity: 0.35; cursor: default; }
.tb-btn-tag:disabled:hover { background: none; color: var(--text-secondary); }

.tag-select-menu, .tag-range-popup {
  position: fixed; z-index: 60; min-width: 240px; max-width: 320px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px; box-shadow: var(--shadow); padding: 5px; display: none;
}
.tag-select-menu.open, .tag-range-popup.open { display: block; }
.tag-select-input-row { padding: 5px 7px; border-bottom: 1px solid var(--divider); margin-bottom: 4px; }
.tag-select-input-row input {
  width: 100%; background: none; border: none; color: var(--text-primary); font-family: inherit; font-size: 13px; outline: none;
}
.tag-select-input-row input::placeholder { color: var(--text-tertiary); }
.tag-select-list { max-height: 220px; overflow-y: auto; }
.tag-select-item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 13px; }
.tag-select-item:hover, .tag-select-item.selected { background: var(--primary-wash); }
.tag-select-item .mi-icon { width: 20px; height: 20px; border-radius: 5px; flex: none; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; background: var(--surface-3); color: var(--text-secondary); }
.tag-select-item .mi-sub { margin-left: auto; font-size: 10.5px; color: var(--text-tertiary); }
.tag-select-item .mi-new { color: var(--text-secondary); font-style: italic; }
.tag-select-hint { padding: 6px 8px 2px; font-size: 10.5px; color: var(--text-tertiary); }

.tag-pop-pills { display: flex; flex-direction: column; gap: 3px; padding: 3px; }
.tag-pill { display: flex; align-items: center; gap: 7px; padding: 6px 7px 6px 9px; border-radius: 6px; background: var(--surface-3); }
.tag-pill-dot { width: 16px; height: 16px; border-radius: 4px; flex: none; display: flex; align-items: center; justify-content: center; font-size: 9.5px; font-weight: 700; }
.tag-pill-dot-entity { background: var(--chip-entity-wash); color: var(--chip-entity); }
.tag-pill-dot-flag { background: var(--chip-flag-wash); color: var(--chip-flag); font-family: var(--font-mono); }
.tag-pill-dot-date { background: var(--chip-date-wash); color: var(--chip-date); }
.tag-pill-label {
  flex: 1; color: var(--text-primary); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: left; background: none; border: none; font-family: inherit; font-size: inherit; cursor: pointer; padding: 0;
}
.tag-pill-label:hover { color: var(--primary-strong); text-decoration: underline; }
.tag-pill-x { width: 18px; height: 18px; border-radius: 4px; border: none; background: none; color: var(--text-tertiary); display: flex; align-items: center; justify-content: center; cursor: pointer; flex: none; }
.tag-pill-x:hover { background: var(--warning); color: #fff; }
.tag-pill-x svg { width: 11px; height: 11px; }

.date-picker { width: 252px; }
.dp-shortcuts { display: flex; gap: 5px; padding: 2px 2px 7px; flex-wrap: wrap; }
.dp-shortcut { border: 1px solid var(--border); background: var(--bg); color: var(--text-secondary); font-size: 11px; padding: 4px 8px; border-radius: 14px; cursor: pointer; }
.dp-shortcut:hover { border-color: var(--chip-date); color: var(--chip-date); }
.dp-nav { display: flex; align-items: center; justify-content: space-between; padding: 2px 2px 6px; }
.dp-nav .dp-month { font-size: 12.5px; font-weight: 700; color: var(--text-primary); }
.dp-nav button { width: 22px; height: 22px; border: none; background: transparent; color: var(--text-tertiary); border-radius: 5px; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.dp-nav button:hover { background: var(--surface-3); color: var(--text-primary); }
.dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; padding: 0 2px; }
.dp-dow { font-size: 9.5px; text-align: center; color: var(--text-tertiary); padding-bottom: 3px; font-weight: 700; letter-spacing: 0.02em; }
.dp-day { aspect-ratio: 1; display: flex; align-items: center; justify-content: center; font-size: 12px; border-radius: 6px; cursor: pointer; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.dp-day:hover { background: var(--chip-date-wash); color: var(--chip-date); }
.dp-day.blank { visibility: hidden; cursor: default; }
.dp-day.today { color: var(--chip-date); font-weight: 700; }
.dp-day.focused { background: var(--chip-date); color: var(--bg); font-weight: 700; }
.dp-hint { padding: 8px 2px 1px; font-size: 10.5px; color: var(--text-tertiary); border-top: 1px solid var(--divider); margin-top: 6px; }
.dp-hint kbd { font-family: var(--font-mono); background: var(--bg); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }

/* ---------- :: / Ctrl+K external link form (shares .mention-menu) ---------- */
.link-form { width: 260px; padding: 4px 2px; }
.link-form-field { margin-bottom: 10px; }
.link-form-field:last-of-type { margin-bottom: 0; }
.link-form-field label { display: block; font-size: 11px; color: var(--text-tertiary); margin-bottom: 4px; }
.link-form-field input {
  width: 100%; background: var(--surface-3); border: 1px solid var(--border); border-radius: 6px;
  padding: 7px 9px; font-size: 13px; color: var(--text-primary); font-family: inherit;
}
.link-form-field input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-wash); }
.link-form-hint { padding: 8px 2px 1px; font-size: 10.5px; color: var(--text-tertiary); border-top: 1px solid var(--divider); margin-top: 4px; }
.link-form-hint kbd { font-family: var(--font-mono); background: var(--bg); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }

.note-link { color: var(--link); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.note-link:hover { color: var(--primary-strong); }

/* Offline-gated context menu entries (Phase 9G) -- shown but inert, so the
   action stays discoverable and its absence isn't mistaken for a bug. */
.context-menu-item.disabled { opacity: 0.4; cursor: default; }
.context-menu-item.disabled:hover { background: transparent; }

/* Phase 9 -- offline / sync status. Amber while offline (work is safe but
   only on this device), muted while syncing. */
.offline-indicator { border-radius: 5px; padding: 0 7px; font-weight: 500; }
.offline-indicator[data-state="offline"] { background: rgba(245, 166, 35, 0.16); color: var(--warning, #F5A623); }
.offline-indicator[data-state="syncing"] { color: var(--text-tertiary); }

/* Phase 9 -- "also open on another device". Deliberately quiet: it's a
   heads-up that a merge may be coming, not something to act on. */
.presence-dot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--primary); opacity: 0.75;
}

/* Sync notifications. Informational only -- never blocks typing. */
.sync-toast {
  position: fixed; bottom: 40px; left: 50%; transform: translateX(-50%) translateY(8px);
  background: var(--surface-3); border: 1px solid var(--border); color: var(--text-primary);
  padding: 8px 14px; border-radius: 8px; font-size: 12.5px; box-shadow: var(--shadow);
  opacity: 0; pointer-events: none; transition: opacity 0.18s ease, transform 0.18s ease; z-index: 60;
}
.sync-toast.open { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Phase 9 -- unresolved merge conflict, shown inline with both versions.
   Amber rather than red: nothing is broken and nothing was lost, it just
   needs a choice. */
.merge-conflict {
  border: 1px solid var(--warning, #F5A623); border-radius: 8px; background: var(--surface-2);
  padding: 10px 12px; margin: 0 0 14px; display: flex; flex-direction: column; gap: 8px;
}
.merge-conflict .mc-head { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--warning, #F5A623); font-weight: 600; }
.merge-conflict .mc-side { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; background: var(--surface-1); border: 1px solid var(--divider); border-radius: 6px; }
.merge-conflict .mc-label { font-size: 11px; color: var(--text-tertiary); }
/* Phase 14 -- the other side is shown as a redline against yours (.mc-redline,
   a view only); the clean copy (.mc-body) stays in the DOM, hidden, for
   "Keep theirs" and serialization. */
.merge-conflict .mc-body-hidden { display: none; }
.merge-conflict .mc-redline > * { margin: 0; }
.merge-conflict .mc-redline ins.diff-add, .merge-conflict .mc-redline del.diff-del { display: inline; border-left: none; padding-left: 0; }
.merge-conflict .mc-redline ins.diff-add { text-decoration: underline; }
.merge-conflict .mc-redline del.diff-del { text-decoration: line-through; }
.merge-conflict .mc-body > * { margin: 0; }
.merge-conflict .mc-keep {
  align-self: flex-start; margin-top: 2px; padding: 4px 10px; font-size: 12px; font-family: inherit;
  border-radius: 6px; border: 1px solid var(--border); background: var(--surface-3);
  color: var(--text-secondary); cursor: pointer;
}
.merge-conflict .mc-keep:hover { border-color: var(--primary); color: var(--text-primary); }

.flash-highlight { animation: flashPulse 1.3s ease-out; border-radius: 5px; }
@keyframes flashPulse { 0% { box-shadow: 0 0 0 4px var(--primary-wash); background: var(--primary-wash); } 100% { box-shadow: 0 0 0 0 transparent; background: transparent; } }
/* Item 6 -- Ctrl/Cmd+S forced-save confirmation only, never the silent normal autosave flow. */
.save-flash { animation: saveFlashGreen 0.9s ease-out; }
@keyframes saveFlashGreen { 0% { background: rgba(74, 222, 128, 0.16); } 100% { background: var(--surface-1); } }

/* ---------- global search dialog (Shift Shift) ---------- */
.search-overlay { position: fixed; inset: 0; z-index: 90; background: rgba(6, 8, 11, 0.55); display: none; align-items: flex-start; justify-content: center; padding-top: 12vh; }
.search-overlay.open { display: flex; }
.search-panel { width: min(620px, 92vw); max-height: 70vh; background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); overflow: hidden; display: flex; flex-direction: column; }
.search-input-row { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.search-input-row svg { width: 16px; height: 16px; color: var(--text-tertiary); flex: none; }
.search-input { flex: 1; background: transparent; border: none; color: var(--text-primary); font-size: 15px; font-family: inherit; }
.search-input:focus { outline: none; }
.search-results { overflow-y: auto; padding: 6px; flex: 1; }
.search-group-title { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-tertiary); padding: 9px 10px 4px; }
.search-result { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 7px; cursor: pointer; }
.search-result.active { background: var(--primary-wash); }
.search-result .sr-icon { width: 24px; height: 24px; border-radius: 6px; flex: none; display: flex; align-items: center; justify-content: center; font-size: 10.5px; font-weight: 700; }
.search-result .sr-icon svg { width: 12px; height: 12px; }
.search-result .sr-icon.entity { background: var(--chip-entity-wash); color: var(--chip-entity); border: 1px solid var(--chip-entity-border); }
.search-result .sr-icon.flag { background: var(--chip-flag-wash); color: var(--chip-flag); border: 1px solid var(--chip-flag-border); font-family: var(--font-mono); }
.search-result .sr-icon.date { background: var(--chip-date-wash); color: var(--chip-date); border: 1px solid var(--chip-date-border); }
.search-result .sr-icon.note, .search-result .sr-icon.text { background: var(--chip-noteref-wash); color: var(--chip-noteref); border: 1px solid var(--chip-noteref-border); }
.search-result .sr-body { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.search-result .sr-title { font-size: 13px; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result .sr-meta { font-size: 11px; color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result mark { background: var(--primary-wash); color: var(--primary-strong); border-radius: 2px; padding: 0 1px; }
.search-result .sr-badge { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--primary-strong); background: var(--primary-wash); border-radius: 3px; padding: 1px 5px; margin-left: 7px; vertical-align: 1px; }
.search-empty { padding: 26px 16px; text-align: center; color: var(--text-tertiary); font-size: 12.5px; }
.search-hint-row { display: flex; gap: 14px; padding: 8px 14px; border-top: 1px solid var(--border); font-size: 10.5px; color: var(--text-tertiary); }
.search-hint-row kbd { font-family: var(--font-mono); background: var(--bg); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }

@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
