/* ══════════════════════════════════════════════════════════════
   EDITOR TOOLBAR (segmented control + word count)
══════════════════════════════════════════════════════════════ */
.editor-toolbar {
  display: flex; align-items: center;
  padding: 6px 16px;
  background: var(--bg-surface); border-bottom: 1px solid var(--border);
  flex-shrink: 0; gap: 12px;
}
.md-segmented {
  display: flex; align-items: center;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 3px; gap: 2px;
}
.md-seg-btn {
  padding: 5px 13px; border-radius: var(--radius-sm);
  font-size: 12px; font-weight: 500;
  color: var(--text-secondary);
  transition: background var(--transition), color var(--transition);
  white-space: nowrap; display: flex; align-items: center; gap: 5px;
}
.md-seg-btn svg { width: 12px; height: 12px; }
.md-seg-btn:hover { color: var(--text-primary); }
.md-seg-btn.active {
  background: var(--bg-surface); color: var(--accent);
  box-shadow: var(--shadow-sm);
}
/* ══════════════════════════════════════════════════════════════
   EDITOR AREA
══════════════════════════════════════════════════════════════ */
.editor-area { flex: 1; display: flex; flex-direction: column; min-height: 0; position: relative; }

/* Floating card editor — fills height, centered with all-around margins.
   overflow:hidden clips textarea/preview to the card border-radius.
   .remote-notice and the add-comment FAB are siblings in .editor-area
   instead of children here, specifically so overflow:hidden doesn't clip
   them. position:relative is only for .comment-margin-layer, a child that
   deliberately DOES want to be clipped to the card (a comment scrolled out
   of view should disappear, not float outside the card). */
.editor-wrap {
  position: relative;
  /* Flex child: fill remaining height inside .editor-area */
  flex: 1;
  min-height: 0;
  /* Floating card look */
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  /* Grid: single-column by default (write or preview mode).
     .mode-split overrides to two columns below.
     Row 1 = formatting toolbar (auto height); Row 2 = editor/preview (fills rest). */
  display: grid;
  /* minmax(0, 1fr), not a bare 1fr — a bare fr track's automatic minimum
     is its content's min-content size, so an unbreakable long line (a URL,
     a fenced code block with no wrap points) could otherwise force this
     whole card wider than its intended measure before overflow:hidden ever
     gets a chance to clip it. minmax(0, ...) lets the track (and this grid
     item's own box) shrink to the specified width regardless, so it's
     always .note-preview/.note-live's own overflow-x:auto that handles
     long content, never the card growing around it. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  gap: 0;
  /* Comfortable writing width on desktop. Split mode overrides below. */
  width: min(100% - 2rem, 900px);
  margin-inline: auto;
  margin-block: 1rem;
}

/* Split mode: two equal columns, wider card to give each pane room. */
.editor-wrap.mode-split {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  width: min(100% - 2rem, 1400px);
}

#note-editor {
  /* Fill the grid cell fully — grid-template-rows handles height */
  width: 100%; height: 100%; min-height: 0;
  padding: 1.25rem 1rem;
  background: var(--bg-surface); border: none; resize: none; overflow-y: auto;
  color: var(--text-primary); font-size: 15px; line-height: 1.7;
  letter-spacing: 0.01em;
  font-family: var(--font-ui); outline: none;
  /* Explicit rather than left to the browser default (which tracks `color`
     anyway) — removes any ambiguity across the 7 themes/OS dark-mode heuristics. */
  caret-color: var(--text-primary);
  transition: font-family var(--transition), box-shadow var(--transition);
}
#note-editor.monospace    { font-family: var(--font-mono); font-size: 14px; }
#note-editor.remote-typing { box-shadow: inset 0 0 0 2px rgba(245,166,35,0.15); }
#note-editor::placeholder  { color: var(--text-muted); }

/* Focus mode — dims everything except a band around the caret. --focus-y/
   --focus-band are set in ui.js from the caret's actual measured pixel
   position; the defaults here only apply before the first measurement. */
#note-editor.focus-mode {
  --focus-y: 50%;
  --focus-band: 48px;
  -webkit-mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.35) 0,
    rgba(0, 0, 0, 0.35) calc(var(--focus-y) - var(--focus-band)),
    rgba(0, 0, 0, 1) calc(var(--focus-y) - var(--focus-band) / 2),
    rgba(0, 0, 0, 1) calc(var(--focus-y) + var(--focus-band) / 2),
    rgba(0, 0, 0, 0.35) calc(var(--focus-y) + var(--focus-band)),
    rgba(0, 0, 0, 0.35) 100%
  );
  mask-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.35) 0,
    rgba(0, 0, 0, 0.35) calc(var(--focus-y) - var(--focus-band)),
    rgba(0, 0, 0, 1) calc(var(--focus-y) - var(--focus-band) / 2),
    rgba(0, 0, 0, 1) calc(var(--focus-y) + var(--focus-band) / 2),
    rgba(0, 0, 0, 0.35) calc(var(--focus-y) + var(--focus-band)),
    rgba(0, 0, 0, 0.35) 100%
  );
}
@media (min-width: 768px) {
  /* Generous padding; max() formula keeps readable text ≤ 800px wide on large panes */
  #note-editor { padding: 2rem max(5%, calc((100% - 800px) / 2)); font-size: 16px; }
}

/* Typewriter mode — top/bottom padding equal to half the editor's own
   viewport height, so the caret's line can always be scrolled to center,
   even on the first/last line. --typewriter-pad is set in ui.js from the
   editor's actual measured clientHeight. */
#note-editor.typewriter-mode {
  padding-top: var(--typewriter-pad, 40vh);
  padding-bottom: var(--typewriter-pad, 40vh);
}

/* Split mode: 1px border divider between editor and preview panes.
   The old .split-mode selector is kept as a fallback alias. */
.editor-wrap.mode-split #note-editor,
.editor-wrap.split-mode #note-editor {
  border-right: 1px solid var(--border);
}

/* Markdown preview */
.note-preview {
  /* Fill the grid cell fully — independent scroll */
  width: 100%; height: 100%; min-height: 0;
  padding: 1.25rem 1rem;
  background: var(--bg-surface); color: var(--text-primary);
  font-size: 15px; line-height: 1.7; letter-spacing: 0.01em;
  font-family: var(--font-ui); overflow-y: auto;
  /* Deliberately no blanket scroll-behavior:smooth here — it would animate
     *every* scrollTop assignment, including the Split-mode proportional
     sync handlers' (ui/editor.js's _wireScrollSync), whose intermediate
     animation-frame scroll events fire after the handler's re-entrancy lock
     has already cleared and get fed back into the opposite pane as if they
     were user scrolling — reintroducing the exact jitter the sync handlers
     are meant to prevent. TOC/footnote anchor clicks get their smoothness
     explicitly instead, via a dedicated click handler (see
     _wireInternalAnchorScroll in ui/editor.js) that calls
     scrollIntoView({behavior:'smooth'}) only for that one interaction. */
}
.note-preview.monospace { font-family: var(--font-mono); font-size: 14px; }
.note-preview.hidden    { display: none; }
@media (min-width: 768px) {
  /* Generous padding; max() keeps readable text ≤ 800px wide on large panes.
     font-size matches #note-editor's and .note-live's own 768px bump so
     Write/Live/Preview stay visually identical at this breakpoint. */
  .note-preview { padding: 2rem max(5%, calc((100% - 800px) / 2)); font-size: 16px; }
}

/* Typora-style editable live-preview surface (CodeMirror 6 mounts inside).
   Same grid-cell fill + padding scheme as .note-preview so switching between
   the rendered pane and the live pane is visually seamless. */
.note-live {
  width: 100%; height: 100%; min-height: 0;
  padding: 1.25rem 1rem;
  background: var(--bg-surface); color: var(--text-primary);
  font-size: 15px; line-height: 1.7; letter-spacing: 0.01em; font-family: var(--font-ui);
  overflow: hidden; /* CM6's own .cm-scroller scrolls */
}
.note-live.hidden { display: none; }
.note-live .cm-editor { height: 100%; }
/* No blanket scroll-behavior:smooth here either, for the same reason as
   .note-preview above — this scroller is also driven by the Split-mode
   sync handlers (live-editor.js's wireScrollSync). The [TOC] widget's own
   click handler opts into smooth scrolling explicitly and only for itself
   (see _scrollPosIntoView's `smooth` option). */
.note-live .cm-md-inlinecode {
  font-family: var(--font-mono); font-size: 0.9em;
  background: var(--bg-elevated); border-radius: 4px; padding: 1px 3px;
}
.note-live .cm-md-highlight {
  background: color-mix(in srgb, var(--yellow) 35%, transparent);
  border-radius: 3px;
}
.note-live .cm-md-checklist-progress {
  display: inline-block; font-size: 11.5px; font-weight: 600;
  color: var(--text-secondary); background: var(--bg-elevated);
  border-radius: 100px; padding: 2px 9px; margin: 0.5em 0 0.2em;
}
.note-live .cm-md-blockquote {
  border-left: 3px solid var(--border);
  padding-left: 0.8em;
  color: var(--text-secondary);
}
/* Nesting depth (> > >) reads as increasing indent, not a flat single
   border — a line inside N nested blockquotes carries every depth's class
   (see the _seamless plugin's Blockquote handling), and since these are
   declared in increasing order, the deepest one wins the cascade. */
.note-live .cm-md-blockquote-2 { padding-left: 1.6em; }
.note-live .cm-md-blockquote-3 { padding-left: 2.4em; }
.note-live .cm-md-blockquote-4 { padding-left: 3.2em; }
/* Fenced code block — background box, applied per-line since this surface
   styles lines rather than wrapping a <pre> around the block (the lines
   stay individually editable). --bg-elevated, not --bg-surface: .note-live
   itself is already --bg-surface (see its own rule above), and a code
   block painted the same colour as the surface it sits on has no visible
   background at all — it used to read as a bare top/bottom rule floating
   over plain text, not a box. --bg-elevated is the same "one step up from
   the surrounding surface" relationship .note-preview pre has against the
   page's own --bg-base. No left/right border on purpose (see the
   blockquote-combination rules below) — a genuinely contrasting background
   band plus top/bottom caps on the first/last line reads just as clearly as
   a boxed border, without a border-left to fight over when the block is
   nested inside a blockquote/alert. */
.note-live .cm-md-codeblock {
  background: var(--bg-elevated);
  padding-left: 16px; padding-right: 16px;
  font-family: var(--font-mono); font-size: 0.9em;
}
.note-live .cm-md-codeblock-first {
  border-top: 1px solid var(--border);
  border-top-left-radius: var(--radius-md); border-top-right-radius: var(--radius-md);
  padding-top: 10px;
}
.note-live .cm-md-codeblock-last {
  border-bottom: 1px solid var(--border);
  border-bottom-left-radius: var(--radius-md); border-bottom-right-radius: var(--radius-md);
  padding-bottom: 10px;
}
/* A fenced code block nested inside a blockquote (or GFM alert) carries
   both the quote's own line class and the codeblock's — since both are
   single-class rules, plain cascade order would let whichever is declared
   last simply clobber the other's padding-left/border-left instead of the
   two combining (confirmed: this is what broke it before this fix). These
   higher-specificity combined selectors make the codeblock's own indent
   additive on top of the quote's, so the block still reads as "inside" the
   quote (its colored left border) while keeping its own background band. */
.note-live .cm-md-blockquote.cm-md-codeblock { padding-left: calc(0.8em + 16px); }
.note-live .cm-md-blockquote-2.cm-md-codeblock { padding-left: calc(1.6em + 16px); }
.note-live .cm-md-blockquote-3.cm-md-codeblock { padding-left: calc(2.4em + 16px); }
.note-live .cm-md-blockquote-4.cm-md-codeblock { padding-left: calc(3.2em + 16px); }
.note-live .cm-md-alert.cm-md-codeblock { padding-left: calc(0.8em + 16px); }
/* Opt-in code-block line numbers — each fenced-code line is already its own
   real .cm-line element here (unlike the static preview, which needs a
   separate gutter overlay to stay Prism-safe — see styles/editor.css's
   .code-line-numbers-gutter), so a plain inline ::before counter is enough:
   no absolute positioning, it just flows before the line's own text. */
body.code-line-numbers .note-live .cm-md-codeblock-first { counter-reset: codeline; }
body.code-line-numbers .note-live .cm-md-codeblock { padding-left: 0; }
/* Only content lines get a visible number — the fence-delimiter lines
   (first/last) keep the box styling but stay un-numbered (see
   cm-md-codeblock-content in live-editor.js). */
body.code-line-numbers .note-live .cm-md-codeblock-content::before {
  counter-increment: codeline; content: counter(codeline);
  display: inline-block; width: 2em; padding-left: 16px; margin-right: 12px;
  text-align: right; color: var(--text-muted); user-select: none;
}
.note-live .cm-md-bullet { color: var(--text-secondary); margin-right: 0.35em; }
.note-live .cm-md-checkbox {
  accent-color: var(--accent);
  margin-right: 0.45em; vertical-align: middle; cursor: pointer;
}
.note-live .cm-md-hr {
  display: inline-block; width: 100%; height: 2px;
  background: var(--border); border-radius: 1px; vertical-align: middle;
}
.note-live .cm-md-image {
  max-width: 100%; height: auto; border-radius: var(--radius-sm);
  display: block; margin: 0.4em 0;
  min-height: 1.6em; min-width: 3em;
  background: var(--bg-elevated);
}
.note-live .cm-md-image.cm-md-image-broken {
  border: 1px dashed var(--border); box-sizing: border-box;
}

/* GFM tables — a real <table>, built from the GFM Table syntax-tree nodes
   markdownLanguage already parses (same GFM extension task lists come from)
   and swapped in as a whole-block replace widget; matches .note-preview's
   own table styling. */
.note-live .cm-md-table-wrap { margin: 0.4em 0; overflow-x: auto; }
.note-live .cm-md-table { width: 100%; border-collapse: collapse; font-size: 0.9em; }
.note-live .cm-md-table th, .note-live .cm-md-table td { border: 1px solid var(--border); padding: 6px 12px; text-align: left; }
.note-live .cm-md-table th { background: var(--bg-elevated); font-weight: 600; }
.note-live .cm-md-table tbody tr:nth-child(even) td { background: var(--bg-elevated); opacity: 0.7; }
/* Editable cell affordance — each cell is its own contenteditable island
   (see live-editor.js's _TableWidget), so a focus ring is the only signal
   a user gets that a cell is directly typeable. */
.note-live .cm-md-table th[contenteditable="true"], .note-live .cm-md-table td[contenteditable="true"] { cursor: text; }
.note-live .cm-md-table th[contenteditable="true"]:hover, .note-live .cm-md-table td[contenteditable="true"]:hover { background: var(--accent-dim); }
.note-live .cm-md-table th[contenteditable="true"]:focus, .note-live .cm-md-table td[contenteditable="true"]:focus {
  outline: 2px solid var(--accent); outline-offset: -2px; background: var(--bg-surface); opacity: 1;
}

/* GitHub-style alerts — > [!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION].
   Matches .note-preview's .md-alert colours; the border lives on the line
   decoration itself (cm-md-alert-<kind>) rather than a wrapping div, since
   this surface styles per-line, not per-block. */
.note-live .cm-md-alert {
  border-left: 3px solid var(--border); padding-left: 0.8em;
}
.note-live .cm-md-alert-title { font-weight: 600; }
.note-live .cm-md-alert-note        { border-left-color: var(--blue); }
.note-live .cm-md-alert-title-note        { color: var(--blue); }
.note-live .cm-md-alert-tip         { border-left-color: var(--green); }
.note-live .cm-md-alert-title-tip         { color: var(--green); }
.note-live .cm-md-alert-important   { border-left-color: var(--accent); }
.note-live .cm-md-alert-title-important   { color: var(--accent); }
.note-live .cm-md-alert-warning     { border-left-color: var(--yellow); }
.note-live .cm-md-alert-title-warning     { color: var(--yellow); }
.note-live .cm-md-alert-caution     { border-left-color: var(--red); }
.note-live .cm-md-alert-title-caution     { color: var(--red); }

/* Footnotes — a superscript reference marker inline, and a small "1."-style
   label on the definition line. No relocated "Footnotes" section here (this
   is an editable surface; the static/export renderer's real footnotes
   section covers that view). */
.note-live .cm-md-footnote-ref {
  font-size: 0.75em; color: var(--accent); font-weight: 600;
}
.note-live .cm-md-footnote-ref-interactive { cursor: pointer; border-radius: 3px; }
.note-live .cm-md-footnote-ref-interactive:hover { text-decoration: underline; }
.note-live .cm-md-footnote-def-marker {
  font-size: 0.85em; font-weight: 600; color: var(--text-secondary);
}

/* Footnote preview popover (src/footnote-popover.js) — click/tap a
   reference to read its text inline instead of jumping to the bottom.
   Shared between the static preview and the Live surface; appended
   directly to <body> (position: absolute against the page, not either
   editor pane) so it isn't clipped by a scrollable pane's overflow. */
.footnote-popover {
  position: absolute; z-index: 50;
  max-width: min(360px, calc(100vw - 24px));
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 10px 12px;
  box-shadow: var(--shadow-md); font-size: 13px; line-height: 1.5;
  animation: footnote-popover-in 0.12s ease;
}
.footnote-popover p:first-child { margin-top: 0; }
.footnote-popover p:last-child { margin-bottom: 0; }
@keyframes footnote-popover-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .footnote-popover { animation: none; }
}

/* Remote collaborator carets (Google-Docs style) */
.note-live .cm-remote-caret {
  position: relative;
  border-left: 2px solid;
  margin-left: -1px;
}
.note-live .cm-remote-caret-label {
  position: absolute;
  top: -1.35em; left: -2px;
  padding: 0 5px;
  border-radius: 3px 3px 3px 0;
  font-size: 10px; font-weight: 600; line-height: 1.5;
  color: #fff; white-space: nowrap;
  pointer-events: none;
  /* Must outrank the scroll rail (z-index 5) and every other in-editor
     float it can render near/under except comments, which now sit above
     this label (z-index 22, see .comment-margin-layer below) by
     deliberate choice — a comment marking a range must never be hidden
     under a collaborator's cursor label passing through that same spot.
     Stays well below the panel/modal/toast tiers (120+) since it's still
     in-document, not a global overlay. */
  z-index: 21;
}
/* A remote collaborator's selected text range — background color set inline
   per-device (color-mix from colorForDevice()) since it's dynamic, same
   pattern as the caret's border/label colors above. */
.note-live .cm-remote-selection { border-radius: 2px; }
@media (min-width: 768px) {
  .note-live { padding: 2rem max(5%, calc((100% - 800px) / 2)); font-size: 16px; }
}
/* No border-left here: #note-editor already carries the split divider via
   its border-right, and doubling them reads as a 2px line. */
.note-live.monospace { font-family: var(--font-mono); font-size: 14px; }
.note-preview h1,.note-preview h2,.note-preview h3,
.note-preview h4,.note-preview h5,.note-preview h6 { margin: 1.2em 0 0.5em; line-height: 1.25; }
.note-preview h1 { font-size: 1.6em; font-weight: 600; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
.note-preview h2 { font-size: 1.35em; font-weight: 600; }
.note-preview h3 { font-size: 1.15em; font-weight: 600; }
.note-preview h4 { font-size: 1.05em; font-weight: 600; }
.note-preview h5,.note-preview h6 { font-size: 1em; font-weight: 600; color: var(--text-secondary); }
.note-preview p  { margin: 0.6em 0; }
.note-preview ul,.note-preview ol { margin: 0.6em 0; padding-left: 1.6em; }
.note-preview li { margin: 0.15em 0; }
.note-preview a  { color: var(--accent); }
.note-preview img { max-width: 100%; height: auto; border-radius: var(--radius-sm); margin: 0.4em 0; display: block; }
.note-preview img.img-broken {
  min-height: 2.4em; min-width: 4em; background: var(--bg-elevated);
  border: 1px dashed var(--border); box-sizing: border-box;
}
.note-preview a.syncpad-file-link-broken {
  color: var(--text-muted); text-decoration: line-through; cursor: default;
}
.note-preview li > ul, .note-preview li > ol { margin: 0.2em 0; }
.note-preview code {
  font-family: var(--font-mono); font-size: 0.9em;
  background: var(--bg-elevated); padding: 1px 6px; border-radius: var(--radius-sm);
}
.note-preview pre {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 14px 16px; overflow-x: auto; margin: 0.8em 0;
  max-width: 100%; /* never wider than the content measure, even on narrow/mobile widths */
  position: relative; /* anchors the line-numbers gutter below, when shown */
}
/* Extra headroom only when a language label is actually present (the badge
   sits at top:8px, always visible — without this, its ~10px height crowds
   right up against the first code line at the default 14px padding-top).
   Blocks with no language (a plain ``` fence) keep the tighter default. */
.note-preview pre.code-block:has(.code-block-lang) { padding-top: 26px; }
body.code-line-numbers .note-preview pre.code-block:has(.code-block-lang) .code-line-numbers-gutter {
  padding-top: 26px;
}
/* Opt-in code-block line numbers (Settings > Editor preferences). A gutter
   *sibling* to <code>, not wrapper spans inside it — Prism.highlightAllUnder()
   replaces <code>'s entire innerHTML with its own tokenized markup for any
   language it recognizes, which would silently destroy per-line wrappers
   living inside it. Hidden by default; toggled on via a body-level class
   the same way read-only-mode is (see src/ui/editor.js's setCodeLineNumbers). */
.note-preview .code-line-numbers-gutter { display: none; }
body.code-line-numbers .note-preview .code-line-numbers-gutter {
  display: block; position: absolute; top: 0; left: 0; bottom: 0;
  width: 2.4em; padding-top: 14px; text-align: right; padding-right: 10px;
  color: var(--text-muted); font-family: var(--font-mono); font-size: 0.85em;
  user-select: none; counter-reset: codeline;
  border-right: 1px solid var(--border);
}
body.code-line-numbers .note-preview .code-line-numbers-gutter span {
  display: block; line-height: 1.55; counter-increment: codeline;
}
body.code-line-numbers .note-preview .code-line-numbers-gutter span::before {
  content: counter(codeline);
}
body.code-line-numbers .note-preview pre.code-block code {
  display: block; padding-left: calc(2.4em + 10px + 10px);
}

/* Language label (top-left, always visible) and copy button (top-right,
   hover/focus-revealed) — both float over the <pre>'s own padding rather
   than reserving a header row, so the code-line-numbers gutter above needs
   no changes to stay aligned. A label is information (useful at a glance
   when scanning several blocks), so it stays visible at rest; copy is an
   action, so it only appears once you're already interacting with the
   block, matching the same at-rest-hidden/reveal-on-approach reasoning as
   the Live surface's document mini-map. */
.code-block-lang {
  position: absolute; top: 8px; left: 16px;
  font-size: 0.68em; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted); opacity: 0.7;
  pointer-events: none; user-select: none;
}
body.code-line-numbers .code-block-lang { left: calc(2.4em + 10px + 10px); }
.code-block-copy-btn {
  position: absolute; top: 6px; right: 6px;
  width: 26px; height: 26px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text-secondary);
  cursor: pointer; opacity: 0;
  transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.code-block-copy-btn svg { width: 14px; height: 14px; }
.code-block-copy-btn .code-block-copy-check { display: none; color: var(--green); }
pre.code-block:hover .code-block-copy-btn,
.code-block-copy-btn:focus-visible {
  opacity: 1;
}
.code-block-copy-btn:hover { background: var(--bg-surface); color: var(--text-primary); border-color: var(--accent); }
.code-block-copy-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Brief confirmation swap after a successful copy (JS toggles this class
   for ~1.4s — see _wirePreviewClickOnce()/file-preview.js's copy handler). */
.code-block-copy-btn.is-copied { opacity: 1; border-color: var(--green); }
.code-block-copy-btn.is-copied .code-block-copy-icon { display: none; }
.code-block-copy-btn.is-copied .code-block-copy-check { display: block; }

/* Table of contents — auto-generated from the note's own headings */
.note-toc {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  margin: 0 0 1.4em;
  padding: 0.6em 0.9em;
  font-size: 0.9em;
}
.note-toc summary {
  cursor: pointer; font-weight: 600; color: var(--text-secondary);
  list-style: none; user-select: none;
}
.note-toc summary::-webkit-details-marker { display: none; }
.note-toc summary::before {
  content: '▸'; display: inline-block; margin-right: 6px;
  transition: transform 0.15s ease;
}
.note-toc details[open] summary::before { transform: rotate(90deg); }
.note-toc ul { list-style: none; margin: 0.6em 0 0; padding: 0; }
.note-toc li { margin: 0.3em 0; overflow-wrap: break-word; word-break: break-word; }
.note-toc a { color: var(--text-primary); text-decoration: none; }
.note-toc a:hover { color: var(--accent); text-decoration: underline; }
.note-toc-h1 { padding-left: 0; font-weight: 600; }
.note-toc-h2 { padding-left: 0.9em; }
.note-toc-h3 { padding-left: 1.8em; }
.note-toc-h4 { padding-left: 2.7em; }
.note-toc-h5 { padding-left: 3.6em; }
.note-toc-h6 { padding-left: 4.5em; }

/* Inline [TOC] marker — Typora-style, rendered in document flow (not the
   floating auto-nav above, which is .note-toc). Same collapsed-by-default
   <details>/<summary> pattern as .note-toc above — native keyboard/touch
   support, no JS needed to toggle. Exported HTML/PDF (src/app/export.js)
   carry their own override forcing the list visible regardless of [open],
   since a collapsed <details> prints/exports empty. */
.md-inline-toc {
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  margin: 0 0 1.4em;
  padding: 0.6em 0.9em;
  font-size: 0.9em;
}
.md-inline-toc summary {
  cursor: pointer; font-weight: 600; color: var(--text-secondary);
  list-style: none; user-select: none;
}
.md-inline-toc summary::-webkit-details-marker { display: none; }
.md-inline-toc summary::before {
  content: '▸'; display: inline-block; margin-right: 6px;
  transition: transform 0.15s ease;
}
.md-inline-toc[open] summary::before { transform: rotate(90deg); }
.md-inline-toc ul { list-style: none; margin: 0.6em 0 0; padding: 0; }
.md-inline-toc li { margin: 0.3em 0; overflow-wrap: break-word; word-break: break-word; }
.md-inline-toc a { color: var(--text-primary); text-decoration: none; }
.md-inline-toc a:hover { color: var(--accent); text-decoration: underline; }
.note-live .cm-md-inline-toc {
  display: block; border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--bg-elevated); padding: 0.6em 0.9em; margin: 0 0 1em; font-size: 0.9em;
}
.note-live .cm-md-inline-toc summary {
  cursor: pointer; font-weight: 600; color: var(--text-secondary);
  list-style: none; user-select: none;
}
.note-live .cm-md-inline-toc summary::-webkit-details-marker { display: none; }
.note-live .cm-md-inline-toc summary::before {
  content: '▸'; display: inline-block; margin-right: 6px;
  transition: transform 0.15s ease;
}
.note-live .cm-md-inline-toc[open] summary::before { transform: rotate(90deg); }
.note-live .cm-md-inline-toc ul { list-style: none; margin: 0.6em 0 0; padding: 0; }
.note-live .cm-md-inline-toc li { margin: 0.3em 0; }
/* pointer-events was previously "none" here, which silently killed every
   Contents entry's click-to-navigate — _TocWidget's own mousedown listener
   (live-editor.js) never fired since the click never reached the <a> at
   all. These links are meant to be the one interactive part of an otherwise
   inert widget (ignoreEvent() returns true for everything else), so they
   need real pointer events, not none. */
.note-live .cm-md-inline-toc a { color: var(--text-primary); text-decoration: none; cursor: pointer; }
.note-live .cm-md-inline-toc a:hover { color: var(--accent); text-decoration: underline; }

/* Scroll rail (src/scroll-rail.js) — a single component merging a draggable
   scroll-position thumb with a heading-tick overview, replacing the native
   scrollbar's *visual* chrome on both editor surfaces. Both the Write
   (.scroll-rail-write) and Live/Preview (.scroll-rail-live) instances mount
   as plain siblings of their surface inside the shared `.editor-wrap` card
   — never inside the surface's own scrolling DOM, and in particular never
   inside CM6's `.cm-editor`, which is CodeMirror-owned internal DOM — and
   both get their top/height/right set inline by ScrollRail's own
   reposition(), computed from their represented surface's live
   getBoundingClientRect() (#note-editor for Write, #note-live for Live —
   NOT .cm-editor, whose responsive padding would put the rail at the wrong
   horizontal position on wide screens). This rule only supplies what's
   common and static: box model, layer, and the touch/coarse-pointer hide —
   never top/height/right, which JS always owns.
   Visibility is derived from each surface's own .hidden class via the
   sibling-combinator rules below, not a separate JS state machine — a mode
   switch that shows/hides #note-editor or #note-live automatically shows/
   hides its rail with it, so there's nothing to keep in sync by hand.
   Two different reveal rules for two different pieces, not one group
   opacity toggle: the thumb (a real scrollbar) stays ambiently visible at
   all times, like any other subtle scrollbar — see .scroll-rail-thumb —
   while the ticks (the heading overview / "minimap") stay fully invisible
   until hovered, and even then fade in individually by distance from the
   pointer rather than all at once (see .scroll-rail-tick's --proximity and
   _onTicksPointerMove in scroll-rail.js) — "only show the dots close to the
   mouse." Mouse-precision-sized and hidden entirely on touch/coarse-pointer
   devices rather than shipping a barely-tappable UI element there, same
   reasoning as the landing demo's own pointer tilt; touch users keep plain
   native scrolling, untouched by any of this. */
.scroll-rail {
  display: none;
  position: absolute; width: 14px;
  z-index: 5; pointer-events: auto;
}
/* This block must come after the bare .scroll-rail rule above — same
   specificity (one class selector each), so source order is what makes
   `display: block` here win over the `display: none` default instead of
   the reverse. */
@media (hover: hover) and (pointer: fine) {
  .scroll-rail { display: block; }
  /* The rail visually replaces the scrollbar it sits over, so the browser's
     own rendering of that scrollbar is hidden underneath it — scrolling
     itself (wheel/keyboard/drag-select) is completely unaffected, only the
     native bar's paint is suppressed. */
  #note-editor, .note-live .cm-scroller { scrollbar-width: none; }
  #note-editor::-webkit-scrollbar, .note-live .cm-scroller::-webkit-scrollbar { display: none; }
}
/* Higher specificity than the bare/media rules above (an id + two classes),
   so these win regardless of source order, in both directions: whichever
   surface is currently hidden (write vs. live/preview — see
   ui/editor.js's setMarkdownMode) hides its own rail right along with it,
   even on a hover-capable device. Split mode shows both surfaces and
   neither of these matches, so both rails stay visible, each already
   positioned over its own column by reposition(). */
#note-editor.hidden ~ .scroll-rail-write,
#note-live.hidden ~ .scroll-rail-live { display: none; }
.scroll-rail.is-unscrollable { pointer-events: none; }
.scroll-rail.is-unscrollable .scroll-rail-thumb { display: none; }

.scroll-rail-thumb {
  position: absolute; right: 4px; width: 6px;
  border-radius: 3px; background: var(--text-muted);
  cursor: grab; pointer-events: auto;
  /* Ambient, not hidden — this is the actual scrollbar; a user shouldn't
     have to hover to find out the document scrolls at all. Subtle enough
     (low opacity, muted color) to disappear into the UI at rest, same
     "considered, not absent" balance as the rest of this component. */
  opacity: 0.32;
  transition: opacity 0.15s ease, background 0.15s ease;
}
.scroll-rail-thumb:hover, .scroll-rail.is-dragging .scroll-rail-thumb {
  opacity: 0.9; background: var(--accent); cursor: grabbing;
}

.scroll-rail-ticks {
  position: absolute; inset: 0; pointer-events: none;
  opacity: 0; transition: opacity 0.15s ease;
}
.scroll-rail:hover .scroll-rail-ticks,
.scroll-rail:focus-within .scroll-rail-ticks,
.scroll-rail.is-dragging .scroll-rail-ticks {
  opacity: 1;
}
.scroll-rail-tick {
  position: absolute; right: 5px; transform: translateY(-50%) scale(calc(0.85 + var(--proximity, 0) * 0.75));
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--text-secondary);
  border: none; padding: 0; cursor: pointer; pointer-events: auto;
  /* --proximity (0–1, set live on pointermove — see scroll-rail.js's
     _onTicksPointerMove) is what makes only the *nearby* ticks stand out
     once the ticks layer is revealed: distant ones sit at a bare-minimum
     0.12 rather than 0, so the eye can still find the full shape of the
     document's structure, but only the tick(s) near the cursor actually
     read as legible/prominent. A short transition smooths the sweep as the
     pointer moves rather than snapping tick-to-tick. */
  opacity: calc(0.12 + var(--proximity, 0) * 0.88);
  transition: opacity 0.08s linear, transform 0.08s linear, background 0.15s ease, box-shadow 0.15s ease;
}
.scroll-rail-tick:hover, .scroll-rail-tick:focus-visible {
  background: var(--accent); transform: translateY(-50%) scale(1.6);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent);
  opacity: 1;
}
.scroll-rail-tick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.note-preview pre code { background: none; padding: 0; font-size: 0.9em; line-height: 1.6; }
.note-preview blockquote { border-left: 3px solid var(--accent); padding-left: 14px; margin: 0.8em 0; color: var(--text-secondary); }
.md-checklist-progress {
  display: inline-block; font-size: 11.5px; font-weight: 600;
  color: var(--text-secondary); background: var(--bg-elevated);
  border-radius: 100px; padding: 2px 9px; margin: 0.5em 0 0.2em;
}
.note-preview hr { border: 0; border-top: 1px solid var(--border); margin: 1.4em 0; }
.note-preview li.md-task { list-style: none; margin-left: -1.4em; padding-left: 0.4em; }
.note-preview li.md-task input[type="checkbox"] { margin-right: 8px; vertical-align: middle; accent-color: var(--accent); cursor: pointer; }
.note-preview li.md-task.md-task-checked { color: var(--text-secondary); text-decoration: line-through; }
body.read-only-mode .note-preview li.md-task input[type="checkbox"],
body.lock-mode .note-preview li.md-task input[type="checkbox"] { cursor: not-allowed; }
/* Tables */
.note-preview table { width: 100%; border-collapse: collapse; margin: 0.8em 0; font-size: 0.9em; }
.note-preview th, .note-preview td { border: 1px solid var(--border); padding: 6px 12px; text-align: left; }
.note-preview th { background: var(--bg-elevated); font-weight: 600; }
.note-preview tbody tr:nth-child(even) td { background: var(--bg-elevated); opacity: 0.7; }
/* Strikethrough */
.note-preview del { color: var(--text-muted); text-decoration-color: var(--text-secondary); }
/* Highlight */
.note-preview mark {
  background: color-mix(in srgb, var(--yellow) 35%, transparent);
  color: var(--text-primary); border-radius: 3px; padding: 0.05em 0.15em;
}
/* GitHub-style alerts — > [!NOTE]/[!TIP]/[!IMPORTANT]/[!WARNING]/[!CAUTION] */
.note-preview .md-alert {
  border-left: 3px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-elevated); padding: 0.7em 1em; margin: 0.8em 0;
}
.note-preview .md-alert-title { font-weight: 600; margin: 0 0 0.4em; }
.note-preview .md-alert > p:last-child { margin-bottom: 0; }
.note-preview .md-alert-note        { border-left-color: var(--blue); }
.note-preview .md-alert-note        .md-alert-title { color: var(--blue); }
.note-preview .md-alert-tip         { border-left-color: var(--green); }
.note-preview .md-alert-tip         .md-alert-title { color: var(--green); }
.note-preview .md-alert-important   { border-left-color: var(--accent); }
.note-preview .md-alert-important   .md-alert-title { color: var(--accent); }
.note-preview .md-alert-warning     { border-left-color: var(--yellow); }
.note-preview .md-alert-warning     .md-alert-title { color: var(--yellow); }
.note-preview .md-alert-caution     { border-left-color: var(--red); }
.note-preview .md-alert-caution     .md-alert-title { color: var(--red); }
/* Footnotes section */
.note-preview .footnotes { margin-top: 2em; padding-top: 0.2em; font-size: 0.9em; color: var(--text-secondary); }
.note-preview .footnotes hr { margin: 0 0 1em; }
.note-preview .footnotes ol { padding-left: 1.4em; }
.note-preview .footnotes li { margin: 0.3em 0; }
.note-preview .footnote-backref { margin-left: 0.3em; text-decoration: none; }
.note-preview sup a { text-decoration: none; }

/* ── Markdown formatting toolbar ────────────────────────────────────────────── */
/* Toolbar lives in row 1 of .editor-wrap's grid; spans all columns in split mode */
.md-toolbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 1px;
  padding: 4px 8px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg-surface);
  flex-wrap: wrap;
  overflow: hidden;
}
/* Hide in preview mode (no editable text to format) */
/* Preview mode hides the toolbar only when the live editable surface didn't
   mount (rendered-HTML fallback, which formatting can't act on). When the
   live surface is up, .live-preview is set and the toolbar stays usable. */
.editor-wrap.mode-preview:not(.live-preview) .md-toolbar { display: none; }
/* Muted in read-only / locked — pointer-events off prevents clicks */
body.read-only-mode .md-toolbar,
body.lock-mode .md-toolbar { opacity: 0.4; pointer-events: none; }

.md-tb-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 26px;
  padding: 0 5px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 600;
  font-family: var(--font-ui);
  cursor: pointer;
  user-select: none;
  flex-shrink: 0;
}
.md-tb-btn:hover { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--border-soft); }
.md-tb-btn:active { background: var(--accent-dim, color-mix(in srgb, var(--accent) 12%, transparent)); color: var(--accent); }
/* Separator between toolbar groups */
.md-tb-sep { width: 1px; height: 18px; background: var(--border-soft); margin: 0 3px; flex-shrink: 0; }
.md-tb-code-icon { font-size: 10px; pointer-events: none; }

/* Remote Notice */
.remote-notice {
  position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 10px 14px;
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
  box-shadow: var(--shadow-md); z-index: 20;
  max-width: min(440px, calc(100vw - 24px));
  animation: slide-up 0.2s ease;
}
.remote-notice.hidden { display: none; }
.remote-notice-label { font-size: 12px; color: var(--text-secondary); text-align: center; }
.remote-notice-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.remote-notice-btn {
  padding: 5px 10px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--bg-base);
  color: var(--accent); font-size: 12px; font-weight: 600; white-space: nowrap;
}
.remote-notice-btn:hover { border-color: var(--accent); background: var(--accent-dim); }
.remote-notice-btn.hidden { display: none; }
@keyframes slide-up {
  from { opacity:0; transform: translateX(-50%) translateY(8px); }
  to   { opacity:1; transform: translateX(-50%) translateY(0); }
}

/* Add-comment floating action button — anchored to the editor pane (see
   .editor-area's position:relative) rather than the page footer, so it
   stays close to whatever's on screen regardless of scroll position or
   document length. Opens a floating composer at the current selection/
   caret — see ui/collab.js's openFloatingCommentComposer(). */
.comment-fab {
  /* --comment-fab-bottom-offset is set/cleared by UI.setCommentFabOffset()
     (src/ui/panels.js) whenever the expiration bar's visibility toggles —
     the bar is an in-flow footer element that doesn't reflow this
     absolutely-positioned FAB on its own, so without the offset the two
     collide on short mobile viewports. Defaults to 0 when the bar is
     hidden. */
  position: absolute; right: 16px;
  bottom: calc(16px + var(--comment-fab-bottom-offset, 0px)); z-index: 19;
  width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--text-inverse);
  box-shadow: var(--shadow-md);
  transition: transform var(--transition), box-shadow var(--transition), bottom var(--transition);
}
.comment-fab:hover { transform: scale(1.06); box-shadow: var(--shadow-lg); }
.comment-fab:active { transform: scale(0.96); }
@media (max-width: 639px) {
  .comment-fab { right: 12px; bottom: calc(12px + var(--comment-fab-bottom-offset, 0px)); width: 36px; height: 36px; }
}

/* Comment margin dots + expandable bubble — the merged cursor-chat +
   comments feature. A small marker at each comment's anchor line,
   positioned by app.js (converts LiveEditor.coordsAtPos()/textarea mirror-
   div measurements to .editor-wrap-relative pixels) so comments are visible
   while scrolling instead of only discoverable via the side panel list.
   Clicking a dot expands a floating bubble at the same position with the
   full comment + Prev/Next/delete — see ui/collab.js's
   renderFloatingComments(). pointer-events:none on the layer, auto on the
   dots/bubble themselves, so the layer never blocks clicks/selection on the
   editor beneath it. Above .scroll-rail's z-index (5), not equal to it: the
   rail is a sibling appended into .editor-wrap at runtime whenever CM6
   mounts, later than this layer's own static markup, so equal z-index would
   leave DOM/paint order deciding the tie — the rail winning and silently
   swallowing clicks meant for a .comment-dot wherever the two happen to
   overlap horizontally (a fine-pointer Live/Split surface with anchored
   comments always has both in the same margin). An explicit lower z-index
   for the rail would fix the same overlap, but comments should render above
   the rail either way — it's chrome for navigating the document, not part
   of its content.

   z-index 22, not just enough to clear the rail (5): comments must render
   above literally everything else that can appear in or over the document
   surface — remote collaborator carets/labels (21, see .cm-remote-caret-label
   above), the comment-fab and remote-notice overlays (19/20), and any CM6
   decoration inside .cm-editor, none of which should ever be able to hide a
   comment dot or its expanded bubble. Still well below the panel/modal/
   toast tiers (120+), which are genuine global overlays rather than
   in-document chrome. */
.comment-margin-layer {
  position: absolute; inset: 0; pointer-events: none; z-index: 22;
}
.comment-dot {
  position: absolute; right: 6px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg-surface);
  pointer-events: auto; cursor: pointer; transform: translateY(-50%);
  transition: transform var(--transition), background var(--transition);
}
.comment-dot:hover, .comment-dot.active { transform: translateY(-50%) scale(1.35); }
/* Hover-driven and mouse-precision-sized (9px) — same rationale as the
   scroll rail's ticks (see .scroll-rail above): hidden on touch/coarse-pointer
   devices rather than shipping a barely-tappable target there. Unlike the
   scroll rail, this isn't a dead end on touch — .cm-comment-anchor spans
   (the dotted-underline text itself) are tap targets there instead (see
   live-editor.js's _isCoarsePointer-gated mousedown handler), so the
   bubble below still needs to render on touch; only the dot disappears.
   Scoped to .live-preview (the CM6 Live/Split surface is actually
   mounted): Write mode's plain textarea has no .cm-comment-anchor spans
   to tap, so the dot stays as the only affordance there — an ordinary
   tap already activates a <button>'s click handler, no special touch
   handling needed. */
@media (hover: none), (pointer: coarse) {
  .editor-wrap.live-preview .comment-dot { display: none; }
}

.comment-floating-bubble {
  position: absolute; right: 22px; transform: translateY(-50%);
  width: 220px; max-width: calc(100vw - 60px);
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 8px 10px;
  box-shadow: var(--shadow-md); pointer-events: auto; z-index: 22;
  font-size: 12.5px; line-height: 1.4;
  /* Own keyframe (not shared with .comment-floating-composer, which animates
     a different transform) so this bubble's `forwards` fill lands back on
     its own translateY(-50%) instead of the composer's translate(-8px,-100%). */
  animation: comment-bubble-in 0.15s ease forwards;
}
/* `top` only transitions for an explicit Prev/Next navigation (see the
   `animate` flag in ui/collab.js's renderFloatingComments) — the bubble's
   position is also recalculated on every plain scroll/resize, and
   transitioning unconditionally made it visibly lag behind its anchor
   during ordinary scrolling instead of only animating on navigation. */
.comment-floating-bubble.comment-bubble-navigating { transition: top 0.18s ease; }
@keyframes comment-bubble-in { from { opacity: 0; transform: translateY(-50%) scale(0.97); } to { opacity: 1; transform: translateY(-50%) scale(1); } }
@media (prefers-reduced-motion: reduce) {
  /* The compound .comment-floating-bubble.comment-bubble-navigating selector
     is more specific than plain .comment-floating-bubble above, so it must
     be named explicitly here too or it'd win over this override. */
  .comment-floating-bubble, .comment-floating-bubble.comment-bubble-navigating { transition: none; animation: none; }
}
.comment-floating-bubble-meta {
  font-size: 10.5px; font-weight: 650; color: var(--text-secondary); margin-bottom: 3px;
}
.comment-floating-bubble-text {
  color: var(--text-primary); overflow-wrap: break-word; word-break: break-word;
}
.comment-floating-bubble-actions {
  display: flex; align-items: center; gap: 4px; margin-top: 6px;
}
.comment-nav-btn {
  width: 20px; height: 20px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; line-height: 1; color: var(--text-secondary);
  background: transparent; border: none; cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.comment-nav-btn:hover { background: var(--bg-surface); color: var(--accent); }
.comment-floating-bubble .comment-delete-btn {
  margin-left: auto; width: 20px; height: 20px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted); background: transparent; border: none; cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.comment-floating-bubble .comment-delete-btn:hover { background: var(--bg-surface); color: var(--red); }

@media (max-width: 639px) {
  /* No margin-dot gutter to hug on mobile (the dot itself is hidden above),
     so the bubble opens from a tapped .cm-comment-anchor span instead —
     give it more breathing room and larger touch targets than the
     mouse-sized desktop version. */
  .comment-floating-bubble {
    right: 10px; width: calc(100vw - 20px);
    font-size: 13.5px; padding: 10px 12px;
  }
  .comment-floating-bubble-meta { font-size: 11.5px; }
  .comment-nav-btn, .comment-floating-bubble .comment-delete-btn {
    width: 30px; height: 30px; font-size: 16px;
  }
}

/* Typing Indicator */
.typing-indicator {
  padding: 3px 20px 4px; font-size: 11px; color: var(--text-muted);
  min-height: 20px; flex-shrink: 0; transition: opacity 0.3s ease; font-style: italic;
}
.typing-indicator.hidden { opacity: 0; }

/* Expiration Bar */
.expiration-bar {
  display: flex; align-items: center; gap: 10px; padding: 7px 16px;
  background: rgba(251,191,36,0.06); border-top: 1px solid rgba(251,191,36,0.18);
  font-size: 12px; color: var(--yellow); flex-shrink: 0;
}
.expiration-bar.hidden { display: none; }
.expiration-bar .exp-time { font-family: var(--font-mono); font-weight: 500; }
.expiration-bar .exp-cancel { margin-left: auto; font-size: 11px; color: var(--text-secondary); transition: color var(--transition); }
.expiration-bar .exp-cancel:hover { color: var(--text-primary); }
.setting-stack  { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
/* Auto-expire's accordion grows the panel's scroll height in place — an
   animated reveal instead of the instant display:none/flex flip (shared by
   the rest of the .hidden utility) softens that jump for the rows below it. */
#setting-exp-controls {
  overflow: hidden;
  max-height: 500px; opacity: 1; margin-top: 10px;
  transition: max-height 0.22s ease, opacity 0.18s ease, margin-top 0.22s ease;
}
#setting-exp-controls.hidden {
  display: flex !important; /* override .hidden's display:none so height can animate */
  max-height: 0; opacity: 0; margin-top: 0; pointer-events: none;
}
.exp-presets    { display: flex; flex-wrap: wrap; gap: 8px; }
.setting-chip   { padding: 6px 10px; border: 1px solid var(--border); border-radius: 999px; font-size: 12px; color: var(--text-secondary); }
.setting-chip.is-active { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); }
.exp-custom-row { display: flex; gap: 8px; }
.exp-custom-input { max-width: 120px; }
.exp-custom-unit  { flex: 1; }
.setting-error    { font-size: 12px; color: var(--red); }
.exp-actions      { display: flex; gap: 8px; flex-wrap: wrap; }
.share-security-notes { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 6px; }
.share-security-chip  { font-size: 11px; padding: 4px 8px; border-radius: 999px; border: 1px solid rgba(251,191,36,0.35); background: rgba(251,191,36,0.12); color: var(--yellow); }

/* Footer */
.app-footer {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 12px;
  background: var(--bg-surface); border-top: 1px solid var(--border); flex-shrink: 0;
}
.app-footer .word-count { font-size: 11px; color: var(--text-muted); font-family: var(--font-mono); }
.footer-tools { display: flex; gap: 2px; }
.footer-tool-btn {
  height: 28px; padding: 0 10px; border-radius: var(--radius-sm);
  font-size: 12px; color: var(--text-secondary);
  display: flex; align-items: center; gap: 5px;
  transition: background var(--transition), color var(--transition);
}
.footer-tool-btn:hover { background: var(--bg-elevated); color: var(--text-primary); }
.footer-tool-btn svg { width: 13px; height: 13px; }

