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

/* ── Theme variables ───────────────────────────────── */
:root {
  --bg-page:     #0b0c0d;
  --bg-raised:   #121316;
  --bg-code:     #16181c;
  --fg:          #d7dbd7;
  --fg-dim:      #6f7478;
  --fg-faint:    #43474b;
  --accent:      #4ec94e;
  --border:      #212429;
  --border-soft: #1a1c20;
  --cursor-color: #4ec94e;
  --warn:        #e5c07b;
  --danger:      #e06c75;

  --accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);
  --accent-line: color-mix(in srgb, var(--accent) 30%, transparent);

  --font-size:    14px;
  /* Chrome text (sidebar + tabline) scales from this one value; every size in those
     two panels is an em fraction of it, so the whole panel resizes together. Kept
     separate from --font-size because the note you are reading and the furniture
     around it want different sizes — that is the point of having two controls. */
  --ui-font-size: 12.5px;
  --line-height:  1.6;
  --status-height: 34px;
  --radius: 6px;
  --col-width: 760px;
  --ease: cubic-bezier(0.25, 0.6, 0.3, 1);
}

:root[data-theme="github-dark"] {
  --bg-page:     #0d1117;
  --bg-raised:   #161b22;
  --bg-code:     #1c2129;
  --fg:          #c9d1d9;
  --fg-dim:      #8b949e;
  --fg-faint:    #484f58;
  --accent:      #58a6ff;
  --border:      #30363d;
  --border-soft: #21262d;
  --cursor-color: #58a6ff;
}

:root[data-theme="nord"] {
  --bg-page:     #2e3440;
  --bg-raised:   #3b4252;
  --bg-code:     #434c5e;
  --fg:          #eceff4;
  --fg-dim:      #a0aab8;
  --fg-faint:    #6b7689;
  --accent:      #88c0d0;
  --border:      #4c566a;
  --border-soft: #3b4252;
  --cursor-color: #88c0d0;
}

:root[data-theme="solarized-light"] {
  --bg-page:     #fdf6e3;
  --bg-raised:   #eee8d5;
  --bg-code:     #e9e2cd;
  --fg:          #586e75;
  --fg-dim:      #839496;
  --fg-faint:    #b0aa93;
  --accent:      #268bd2;
  --border:      #ccc4b0;
  --border-soft: #ddd6c1;
  --cursor-color: #268bd2;
}

:root[data-theme="dracula"] {
  --bg-page:     #1e1e2e;
  --bg-raised:   #282a36;
  --bg-code:     #21222c;
  --fg:          #f8f8f2;
  --fg-dim:      #8a93b5;
  --fg-faint:    #6272a4;
  --accent:      #bd93f9;
  --border:      #44475a;
  --border-soft: #313341;
  --cursor-color: #f1fa8c;
}

:root[data-theme="one-dark"] {
  --bg-page:     #1e2127;
  --bg-raised:   #21252b;
  --bg-code:     #282c34;
  --fg:          #abb2bf;
  --fg-dim:      #828997;
  --fg-faint:    #5c6370;
  --accent:      #61afef;
  --border:      #3a3f4b;
  --border-soft: #2c313a;
  --cursor-color: #61afef;
}

:root[data-theme="tokyo-night"] {
  --bg-page:     #1a1b26;
  --bg-raised:   #1f2335;
  --bg-code:     #24283b;
  --fg:          #c0caf5;
  --fg-dim:      #8189af;
  --fg-faint:    #565f89;
  --accent:      #7aa2f7;
  --border:      #3b4261;
  --border-soft: #292e44;
  --cursor-color: #7aa2f7;
}

:root[data-theme="github-light"] {
  --bg-page:     #ffffff;
  --bg-raised:   #f6f8fa;
  --bg-code:     #f0f3f6;
  --fg:          #24292f;
  --fg-dim:      #57606a;
  --fg-faint:    #8c959f;
  --accent:      #0969da;
  --border:      #d0d7de;
  --border-soft: #e4e8ec;
  --cursor-color: #0969da;
}

:root[data-theme="atom-one-light"] {
  --bg-page:     #fafafa;
  --bg-raised:   #f0f0f0;
  --bg-code:     #eaeaeb;
  --fg:          #383a42;
  --fg-dim:      #696c77;
  --fg-faint:    #a0a1a7;
  --accent:      #4078f2;
  --border:      #dcdce0;
  --border-soft: #e8e8ea;
  --cursor-color: #4078f2;
}

:root[data-theme="gruvbox-light"] {
  --bg-page:     #fbf1c7;
  --bg-raised:   #f2e5bc;
  --bg-code:     #ecdfb0;
  --fg:          #3c3836;
  --fg-dim:      #665c54;
  --fg-faint:    #a89984;
  --accent:      #af3a03;
  --border:      #d5c4a1;
  --border-soft: #e5d9b3;
  --cursor-color: #af3a03;
}

:root[data-theme="solarized-dark"] {
  --bg-page:     #002b36;
  --bg-raised:   #073642;
  --bg-code:     #05303b;
  --fg:          #93a1a1;
  --fg-dim:      #839496;
  --fg-faint:    #586e75;
  --accent:      #268bd2;
  --border:      #0a4351;
  --border-soft: #073642;
  --cursor-color: #268bd2;
}

:root[data-theme="gruvbox-dark"] {
  --bg-page:     #282828;
  --bg-raised:   #3c3836;
  --bg-code:     #32302f;
  --fg:          #ebdbb2;
  --fg-dim:      #bdae93;
  --fg-faint:    #928374;
  --accent:      #fabd2f;
  --border:      #504945;
  --border-soft: #3c3836;
  --cursor-color: #fabd2f;
}

:root[data-theme="catppuccin-mocha"] {
  --bg-page:     #1e1e2e;
  --bg-raised:   #181825;
  --bg-code:     #11111b;
  --fg:          #cdd6f4;
  --fg-dim:      #a6adc8;
  --fg-faint:    #6c7086;
  --accent:      #89b4fa;
  --border:      #45475a;
  --border-soft: #313244;
  --cursor-color: #89b4fa;
}

/* ── Page layout ───────────────────────────────────── */
html, body {
  height: 100%;
  background: var(--bg-page);
  color: var(--fg);
  overflow-x: hidden;
}

/* thin, theme-aware scrollbars */
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 5px;
  border: 2px solid var(--bg-page);
}
::-webkit-scrollbar-thumb:hover { background: var(--fg-faint); }

body {
  display: flex;
  flex-direction: column;
  font-size: var(--font-size);
  line-height: var(--line-height);
}

button { font-family: inherit; }

/* ── Document container ────────────────────────────── */
#document-container {
  /* flex: 1 is dead now that #app-shell is a grid (grid items ignore flex; sizing
     comes from #app-shell's own flex: 1 + grid stretch instead) — kept as a harmless
     no-op documenting the pre-sidebar layout; don't remove #app-shell's flex: 1 thinking
     this is the one doing the work. */
  flex: 1;
  overflow-y: auto;
  padding: 40px 24px 32px;
  position: relative;
  scroll-behavior: smooth;
}

/* The buffer hugs the left, vim-style, instead of floating in a centred column:
   centring pushed the line-number gutter into the middle of the screen and left a
   dead strip between the sidebar and the text. padding-left reserves the gutter. */
/* padding-right reserves the row-action lane the same way, now that the hover
   controls sit on that side. Without it they hang past the container's content box
   and, because overflow-y:auto makes overflow-x auto too, drag a horizontal
   scrollbar onto the note. The column hugs left via margin-right:auto, so at normal
   widths this only eats slack — nothing moves. */
#document-container { padding-left: 78px; padding-right: 112px; }
#doc-col, #document-container > .block, #document-container > * {
  max-width: var(--col-width);
  margin-left: 0;
  margin-right: auto;
}
/* No line-number gutter to reserve room for when the numbers are hidden, so both
   lanes collapse. The hover controls do NOT go away here — `@media (hover: none)`
   hides them on touch, but a narrow window on a mouse still has them — so they tuck
   inside the column instead of hanging past it. Left outside, they added 76px of
   overflow and put a horizontal scrollbar on the note. */
@media (max-width: 820px) { #document-container { padding-left: 24px; padding-right: 24px; } }

/* ── Block base ────────────────────────────────────── */
.block {
  width: 100%;
  position: relative;
  border-radius: var(--radius);
  transition: background 0.15s var(--ease);
}
/* Consecutive text blocks must read as one document, not a stack of cards, so no
   inter-block gap. Code blocks re-introduce their own margin below — they are a
   genuinely separate object and should still sit apart. */
.block + .block { margin-top: 0; }

@media (hover: hover) {
  /* Only code blocks get a hover tint. Tinting a text block advertised a boundary
     that shouldn't exist once prose flows continuously (issue #30 follow-on). */
  .code-block:hover { background: color-mix(in srgb, var(--fg) 2.5%, transparent); }
}

/* hover gutter controls */
/* Right of the row, not left. The buffer hugs the left edge vim-style, so a gutter
   at `left: -104px` sat between the line numbers and the text — reading as part of
   the document rather than as controls acting on it. The empty space is all on the
   right, which is also where the eye goes looking for row actions (the sidebar's
   rename/delete live there too). */
.gutter {
  position: absolute;
  right: -104px;
  /* Aligned to the first line box now that .block-content has no top padding;
     6px was measured against the old 8px-padded block. */
  top: -1px;
  display: flex;
  gap: 2px;
  opacity: 0;
  /* Slides outward, away from the text — mirrored from the old left-hand version,
     which started shifted right for the same reason. */
  transform: translateX(-4px);
  transition: opacity 0.15s var(--ease), transform 0.15s var(--ease);
  z-index: 5;
}
.block:hover .gutter,
.gutter:hover { opacity: 1; transform: none; }
.gutter button {
  width: 23px;
  height: 23px;
  display: grid;
  place-items: center;
  background: var(--bg-raised);
  color: var(--fg-dim);
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.gutter button:hover { color: var(--fg); border-color: var(--border); }
.gutter button[data-act="del"]:hover { color: var(--danger); border-color: var(--danger); }
@media (hover: none) { .gutter { display: none; } }
/* Below 820px the reserved right lane collapses (see #document-container above), so
   the controls tuck inside the column's right edge instead of hanging past it and
   dragging a horizontal scrollbar onto the note. They keep their raised background,
   so they stay readable over the line they overlap — and they only appear on hover.
   This has to live AFTER the base .gutter rule: a media query adds no specificity,
   so declaring it up beside the padding change lost to the `right: -104px` below it. */
@media (max-width: 820px) { .gutter { right: 4px; } }

/* touch toolbar (mobile) */
.touch-tools {
  display: none;
  gap: 4px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px;
  margin: 0 0 6px 4px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  width: max-content;
}
.touch-tools button {
  min-width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  background: transparent;
  border: none;
  border-radius: 4px;
  color: var(--fg-dim);
  font-size: 14px;
  padding: 0 8px;
}
.touch-tools button:active { background: color-mix(in srgb, var(--fg) 6%, transparent); color: var(--fg); }
.touch-tools button[data-act="del"] { color: var(--danger); }
@media (hover: none) {
  .block.active .touch-tools { display: inline-flex; }
}

/* ── Text block ────────────────────────────────────── */
.text-block .block-content {
  background: transparent;
  color: var(--fg);
  /* No vertical padding: with it, consecutive blocks sat 16px apart while lines
     inside a block sat at line-height — the visual tell of a block editor. */
  padding: 0 16px;
  min-height: calc(var(--font-size) * var(--line-height));
  white-space: pre-wrap;
  word-wrap: break-word;
  outline: none;
  caret-color: var(--cursor-color);
  border: none;
}

/* placeholder only on a fresh note (single empty block), not every empty block */
.text-block:only-child .block-content:empty::before {
  content: 'let ideas flow...';
  color: var(--fg);
  opacity: 0.22;
  pointer-events: none;
}

/* rendered markdown layer — shown when block not being edited */
.md-layer {
  display: none;
  /* Must match .block-content exactly — the two layers swap in place, and any
     difference makes text jump when a block gains or loses focus. */
  padding: 0 16px;
  min-height: calc(var(--font-size) * var(--line-height));
  cursor: text;
  word-wrap: break-word;
}
.text-block.has-md:not(.editing) .md-layer { display: block; }
.text-block.has-md:not(.editing) .block-content { display: none; }

.md-mark { color: var(--fg-faint); font-weight: 400; }
.md-h { font-weight: 600; color: color-mix(in srgb, var(--fg) 92%, white); letter-spacing: -0.015em; }
/* Headings keep their larger glyphs but occupy exactly ONE base line, so the line
   box is identical to the raw text the editable shows in its place. line-height is
   the base 1.6 divided by each font-size (1.6/1.5, 1.6/1.25, 1.6/1.1) and the margins
   are gone, because either would make the rendered line taller than the editable one
   and the block would resize as it gained or lost focus.

   It also keeps every line the same height as its number in the gutter — in an editor
   that shows line numbers, a heading that is 1.45 rows tall puts every number below it
   slightly out of step with the line it labels. */
.md-h1 { font-size: 1.5em;  line-height: calc(var(--line-height) / 1.5);  margin: 0; }
.md-h2 { font-size: 1.25em; line-height: calc(var(--line-height) / 1.25); margin: 0; }
.md-h3 { font-size: 1.1em;  line-height: calc(var(--line-height) / 1.1);  margin: 0; }
.md-p { min-height: 1.6em; }
.md-li { padding-left: 4px; }
.md-li .md-mark { color: var(--accent); }
.md-divider {
  height: 1.6em;
  position: relative;
}
.md-divider::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 1px;
  background: var(--border);
}
.md-check { display: flex; align-items: baseline; gap: 9px; }
.md-check .cb {
  display: inline-grid;
  place-items: center;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 3px;
  font-size: 9px;
  color: var(--accent);
  cursor: pointer;
  transform: translateY(2px);
  transition: border-color 0.15s;
}
.md-check .cb:hover { border-color: var(--accent); }
.md-check.done .cb { background: var(--accent-soft); border-color: var(--accent-line); }
.md-check.done { color: var(--fg-faint); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--fg) 25%, transparent); }
.md-img {
  display: flex;
  padding: 4px 0;
}
.md-img.left   { justify-content: flex-start; }
.md-img.center { justify-content: center; }
.md-img.right  { justify-content: flex-end; }
.md-img .img-box {
  position: relative;
  display: inline-block;
  max-width: 100%;
  z-index: 2;               /* dragged images ride above neighboring text */
}
.md-img.free { overflow: visible; }
.md-img img {
  display: block;
  max-width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--border-soft);
  cursor: grab;
  user-select: none;
}
.md-img img.dragging { cursor: grabbing; opacity: 0.85; }
.md-img .img-handle {
  position: absolute;
  right: -7px;
  bottom: -7px;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: var(--accent);
  border: 2px solid var(--bg-page);
  cursor: nwse-resize;
  touch-action: none;
  opacity: 0;
  transition: opacity 0.15s;
}
.md-img .img-box:hover .img-handle { opacity: 1; }
.md-img .img-rotate {
  position: absolute;
  right: -7px;
  top: -7px;
  width: 16px;
  height: 16px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--bg-raised);
  border: 1px solid var(--accent-line);
  color: var(--accent);
  font-size: 10px;
  cursor: ew-resize;
  touch-action: none;
  user-select: none;
  opacity: 0;
  transition: opacity 0.15s;
}
.md-img .img-box:hover .img-rotate { opacity: 1; }
@media (hover: none) { .md-img .img-handle, .md-img .img-rotate { opacity: 1; } }

.md-code {
  background: var(--bg-code);
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  padding: 1px 6px;
  font-size: 0.92em;
  color: var(--accent);
}
strong { color: color-mix(in srgb, var(--fg) 92%, white); }
.md-layer del { color: var(--fg-faint); }
.md-layer mark, .cmd-ico.hl-yellow, .cmd-ico.hl-green, .cmd-ico.hl-red, .cmd-ico.hl-blue {
  color: inherit;
  padding: 0 3px;
  border-radius: 3px;
}
.hl-yellow { background: color-mix(in srgb, #e5c07b 30%, transparent) !important; }
.hl-green  { background: color-mix(in srgb, #4ec94e 25%, transparent) !important; }
.hl-red    { background: color-mix(in srgb, #e06c75 30%, transparent) !important; }
.hl-blue   { background: color-mix(in srgb, #61afef 30%, transparent) !important; }

/* ── Code block ────────────────────────────────────── */
.code-block {
  margin: 10px 0;
  background: var(--bg-code);
  border: 1px solid var(--border-soft);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius);
  overflow: hidden;
  position: relative;
}

.code-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--fg) 1.5%, transparent);
  position: relative;
  z-index: 2;
}
.lang-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  border: none;
  padding: 2px 8px;
  border-radius: 3px;
  cursor: pointer;
}
.lang-badge svg { width: 12px; height: 12px; flex-shrink: 0; }
.lang-badge .caret { opacity: 0.6; margin-left: 1px; }
.line-count { font-size: 10px; color: var(--fg-faint); }
.code-head .head-actions {
  margin-left: auto;
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.15s var(--ease);
}
.code-block:hover .head-actions,
.code-block.active .head-actions { opacity: 1; }
.code-head .head-actions button {
  font-size: 10px;
  color: var(--fg-dim);
  background: transparent;
  border: 1px solid var(--border-soft);
  border-radius: 3px;
  padding: 2px 8px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.code-head .head-actions button:hover { color: var(--fg); border-color: var(--border); }

.code-body { position: relative; }

/* Two-layer: highlight behind, contenteditable in front.
   The two must lay text out IDENTICALLY — the front layer is transparent and
   contributes only the caret, so any metric that differs between them shows up as a
   caret parked away from the glyph it belongs to. */
.hljs-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 10px 14px;
  margin: 0;
  /* Load-bearing, and easy to lose: this is a <pre>, and the UA stylesheet sets
     `pre { font-family: monospace }`. That default beats inheritance, so without
     this line the highlight layer renders in the browser's stock monospace while the
     editable above it uses the app font — two different sets of glyph metrics under
     one line-height. The caret sat 1.5px above the character it marked, and the
     `.hljs-layer code { font-family: inherit }` below faithfully inherited the wrong
     font, which is what made it look like the font WAS being applied. */
  font-family: inherit;
  font-size: calc(var(--font-size) - 1px);
  line-height: 1.7;
  white-space: pre-wrap;
  word-wrap: break-word;
  pointer-events: none;
  background: transparent;
  z-index: 0;
  overflow: hidden;
}

.hljs-layer code {
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  background: transparent;
  padding: 0;
  white-space: pre-wrap;
  word-wrap: break-word;
}

/* The highlight layer wears hljs's own class so each theme's base `.hljs { color }`
   reaches the tokens hljs does not classify. That class also drags in the theme's
   chrome, which has to be undone here — and the selector has to out-specify what the
   theme actually uses, which is NOT plain `.hljs`:

     pre code.hljs { padding: 1em; … }   (0,1,2)  ← the one that bites
     code.hljs     { padding: 3px 5px }  (0,1,1)
     .hljs         { color; background } (0,1,0)  ← the one we want

   `.hljs-layer code` is only (0,1,1), so `pre code.hljs` beat it and the padding came
   back — which is not cosmetic: it shifts this layer's text out from under the
   transparent editable and puts the caret off its glyph again, the very bug the
   font-family line above fixes. Two classes plus the element is (0,2,1) and clears
   all three. `color` is deliberately left alone; inheriting it is the entire point. */
.hljs-layer code.hljs { background: transparent; padding: 0; }

.code-block .block-content {
  position: relative;
  z-index: 1;
  background: transparent;
  color: transparent;
  caret-color: var(--cursor-color);
  padding: 10px 14px;
  min-height: 2.6em;
  font-size: calc(var(--font-size) - 1px);
  line-height: 1.7;
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-wrap: break-word;
  width: 100%;
  overflow-x: hidden;
  outline: none;
}

/* ── Status bar ────────────────────────────────────── */
#status {
  height: var(--status-height);
  background: color-mix(in srgb, var(--bg-page) 96%, black);
  border-top: 1px solid var(--border-soft);
  display: flex;
  align-items: center;
  padding: 0 12px;
  gap: 6px;
  font-size: 11.5px;
  color: var(--fg-dim);
  flex-shrink: 0;
  z-index: 10;
}
#status .seg {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 4px;
  transition: background 0.15s;
  white-space: nowrap;
}
#status .seg:hover { background: color-mix(in srgb, var(--fg) 5%, transparent); }
#status .seg.mode {
  color: var(--accent);
  background: var(--accent-soft);
  font-weight: 500;
}
#status .spacer { flex: 1; }
#status-url { cursor: pointer; }
#status-url .bar {
  display: inline-block;
  width: 54px;
  height: 4px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  position: relative;
  overflow: hidden;
}
#status-url .bar i {
  position: absolute;
  inset: 0;
  width: 0%;
  background: var(--accent);
  border-radius: 2px;
  transition: width 0.3s var(--ease), background 0.3s;
}
#status-url.amber .bar i { background: var(--warn); }
#status-url.red .bar i   { background: var(--danger); }

#status-hint { color: var(--fg-faint); font-size: 11px; }

#status-copied {
  color: var(--accent);
  font-size: 11px;
  opacity: 0;
  transition: opacity 0.2s;
  white-space: nowrap;
}
#status-copied.visible { opacity: 1; }

/* ── Empty state ───────────────────────────────────── */
#empty-state {
  position: absolute;
  top: 0; left: 0; right: 0;
  bottom: var(--status-height);
  z-index: 50;
  overflow-y: auto;
  /* Own stacking context, so ::before/::after at negative z-index land above this
     element's own background instead of disappearing behind the page. */
  isolation: isolate;
  background:
    radial-gradient(ellipse 70% 45% at 50% -5%, color-mix(in srgb, var(--accent) 6%, transparent), transparent),
    var(--bg-page);
  opacity: 1;
  transition: opacity 0.35s var(--ease);
}
#empty-state.hidden { display: none; }
#empty-state.dissolving { opacity: 0; pointer-events: none; }

.empty-center {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 48px 24px 40px;
  text-align: center;
}
.wordmark {
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: color-mix(in srgb, var(--fg) 92%, white);
}
.wordmark .bye { color: var(--fg-faint); font-weight: 300; }
.wordmark .cursor-blink {
  display: inline-block;
  width: 13px;
  height: 26px;
  background: var(--accent);
  margin-left: 6px;
  vertical-align: -3px;
  animation: blink 1.1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.tagline {
  margin-top: 12px;
  color: var(--fg-dim);
  font-size: 13.5px;
}
.tagline b { color: var(--fg); font-weight: 500; }
.tagline a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dashed var(--accent-line);
}

.hint-cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  margin-top: 38px;
  width: 100%;
  max-width: 720px;
}
.hint-card {
  background: var(--bg-raised);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 15px 14px 13px;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.18s var(--ease), transform 0.18s var(--ease);
}
.hint-card:hover { border-color: var(--border); transform: translateY(-2px); }
.hint-card kbd {
  font-family: inherit;
  font-size: 11px;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: 4px;
  padding: 2px 7px;
}
.hint-card .h { margin-top: 9px; font-size: 12.5px; color: var(--fg); font-weight: 500; }
.hint-card .d { margin-top: 2px; font-size: 11px; color: var(--fg-faint); line-height: 1.5; }

.recent {
  width: 100%;
  max-width: 720px;
  margin-top: 34px;
  text-align: left;
}
.recent.hidden { display: none; }
.rc-title {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.rc-title::after { content: ''; flex: 1; height: 1px; background: var(--border-soft); }
.recent-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.recent-item:hover { background: var(--bg-raised); border-color: var(--border-soft); }
/* Keyboard selection — a persistent form of the hover state. */
.recent-item.kb-active,
.recent-folder.kb-active { background: var(--bg-raised); border-color: var(--border-soft); }
.recent-item.kb-active .ri-del,
.recent-item.kb-active .ri-folder { opacity: 1; }
.recent-item .ri-ico { color: var(--fg-faint); font-size: 12px; }
.recent-item .ri-name {
  color: var(--fg);
  font-size: 12.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recent-item .ri-langs { color: var(--fg-faint); font-size: 10.5px; white-space: nowrap; }
.recent-item .ri-langs b { color: var(--accent); font-weight: 400; }
.recent-item .ri-time { margin-left: auto; font-size: 10.5px; color: var(--fg-faint); white-space: nowrap; }
.recent-item .ri-del {
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--fg-faint);
  font-size: 11px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}
.recent-item:hover .ri-del { opacity: 1; }
.recent-item .ri-del:hover { color: var(--danger); border-color: var(--danger); }

.recent-item .ri-folder {
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--fg-faint);
  font-size: 11px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}
.recent-item:hover .ri-folder { opacity: 1; }
.recent-item .ri-folder:hover { color: var(--accent); border-color: var(--accent-line); }
.recent-item .ri-folder svg { width: 14px; height: 14px; display: block; }
@media (hover: none) { .recent-item .ri-del, .recent-item .ri-folder { opacity: 1; } }

/* Dark, palette-matched tooltip for the move-to-folder button (replaces native title). */
.recent-item .ri-folder[data-tip] { position: relative; }
.recent-item .ri-folder[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 7px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-code);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 4px 8px;
  font-size: 10.5px;
  white-space: nowrap;
  letter-spacing: 0.02em;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
}
.recent-item .ri-folder[data-tip]:hover::after,
.recent-item .ri-folder[data-tip]:focus-visible::after { opacity: 1; }

.recent-folder {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 12px 4px;
  cursor: pointer;
  user-select: none;
  font-size: 11.5px;
  color: var(--fg-dim);
}
.recent-folder:hover .rf-name { color: var(--fg); }
.recent-folder .rf-caret { color: var(--accent); font-size: 10px; }
.recent-folder .rf-name { letter-spacing: 0.03em; }
.recent-folder .rf-count { font-size: 10px; color: var(--fg-faint); }
.recent-item.in-folder { margin-left: 18px; }

.creator-credit {
  margin-top: 40px;
  font-size: 11px;
  color: var(--fg-faint);
  letter-spacing: 0.04em;
}
.creator-credit a {
  color: var(--fg-dim);
  text-decoration: none;
  border-bottom: 1px dashed var(--border);
  transition: color 0.15s, border-color 0.15s;
}
.creator-credit a:hover { color: var(--accent); border-color: var(--accent-line); }

/* ── Command palette ───────────────────────────────── */
#palette-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--bg-page) 40%, transparent);
  backdrop-filter: blur(1.5px);
}
#palette-overlay.hidden { display: none; }
#palette-overlay.preview { background: transparent; backdrop-filter: none; }

#palette {
  position: absolute;
  bottom: calc(var(--status-height) + 10px);
  left: 16px;
  width: 340px;
  background: color-mix(in srgb, var(--bg-raised) 97%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 24px 60px rgba(0,0,0,0.55), 0 0 0 1px var(--accent-soft);
}
#palette.anchored { bottom: auto; }

/* ── Floating-window modes (/settings, /help) ───────────────────────────────
   lazy.nvim's shape: a centred bordered frame with its name sitting on the top
   border. Same palette element and same keyboard model — only the frame moves,
   so every existing mode keeps working untouched. */
#palette.float {
  bottom: auto; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(720px, calc(100vw - 48px));
  max-height: min(620px, calc(100vh - 120px));
  display: flex; flex-direction: column;
  overflow: visible;                 /* the title sits on the border, don't clip it */
  border-radius: 10px;
}
#palette.float::before {
  content: attr(data-title);
  position: absolute; top: -9px; left: 22px; padding: 0 9px;
  background: var(--bg-raised); color: var(--accent);
  font-size: 12px; font-weight: 600; letter-spacing: .5px;
}
#palette.float #palette-search-row { border-radius: 10px 10px 0 0; }
#palette.float #palette-list {
  flex: 1; max-height: none; overflow-y: auto;
  padding-bottom: 6px;
}
#palette.float #palette-title { padding-top: 14px; }

/* Drag bars — the real sliders, only rendered in the settings float. */
#palette-bars { padding: 6px 16px 12px; border-top: 1px solid var(--border-soft); flex: 0 0 auto; }
#palette-bars.hidden { display: none; }
.pal-bar { display: flex; align-items: center; gap: 12px; height: 28px; }
.pal-bar label { width: 92px; flex: 0 0 auto; color: var(--fg-dim); font-size: 12px; }
.pal-bar input[type="range"] {
  flex: 1; appearance: none; -webkit-appearance: none;
  height: 4px; border-radius: 3px; background: var(--border); outline: 0; cursor: ew-resize;
}
/* ── Where the arrow keys are ────────────────────────────────────────────────
   The settings panel is fully arrow-navigable, so every stop needs to say so.
   The sliders said nothing at all: `outline: 0` above kills the UA ring (it has
   to — the default ring around a 4px track is ugly), and nothing replaced it, so
   nine identical sliders gave no clue which one ↑/↓ would move.

   The row highlights rather than the track, matching the command rows above —
   which are the stops the eye already reads correctly. A ring around a 4px-tall
   line is a hairline; a lit row is unmissable, and it lights the label too, which
   is the part naming what you are about to change. */
.pal-bar:focus-within {
  background: var(--accent-soft);
  border-radius: 6px;
  box-shadow: 0 0 0 6px var(--accent-soft);   /* pads the row without moving it */
}
.pal-bar:focus-within label,
.pal-bar:focus-within output { color: var(--fg); }
.pal-bar input[type="range"]:focus-visible { background: color-mix(in srgb, var(--accent) 35%, var(--border)); }
/* The thumb grows on focus: the row says which slider, the thumb says where in it. */
.pal-bar input[type="range"]:focus-visible::-webkit-slider-thumb { width: 14px; height: 20px; }
.pal-bar input[type="range"]:focus-visible::-moz-range-thumb { width: 14px; height: 20px; }
.pal-bar input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 10px; height: 16px; border: 0; border-radius: 2px;
  background: var(--accent); cursor: ew-resize;
}
.pal-bar input[type="range"]::-moz-range-thumb {
  width: 10px; height: 16px; border: 0; border-radius: 2px;
  background: var(--accent); cursor: ew-resize;
}
.pal-bar output {
  width: 54px; flex: 0 0 auto; text-align: right;
  color: var(--fg-dim); font-size: 11.5px; font-variant-numeric: tabular-nums;
}
.pal-bars-hint { padding: 2px 16px 10px; color: var(--fg-faint); font-size: 11px; }

@media (max-width: 620px) {
  #palette.float { width: calc(100vw - 24px); max-height: calc(100vh - 90px); }
  .pal-bar label { width: 70px; }
}

#palette-search-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 13px;
  border-bottom: 1px solid var(--border-soft);
}
#palette-search-row .slash { color: var(--accent); font-size: 13px; }
#palette-search {
  flex: 1;
  display: block;
  background: transparent;
  border: none;
  padding: 7px 0;
  color: var(--fg);
  font-family: inherit;
  font-size: 13px;
  outline: none;
  caret-color: var(--cursor-color);
}
#palette-search::placeholder { color: var(--fg-faint); }

#palette-title {
  padding: 8px 13px 2px;
  font-size: 9.5px;
  color: var(--fg-faint);
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
#palette-title:empty { display: none; }

#palette-list {
  list-style: none;
  padding: 3px 6px 6px;
  max-height: 320px;
  overflow-y: auto;
}

#palette-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border-radius: 5px;
  cursor: pointer;
  font-size: 12.5px;
  color: var(--fg-dim);
}
#palette-list li .cmd-ico {
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: 4px;
  background: color-mix(in srgb, var(--fg) 4%, transparent);
  color: var(--fg-dim);
  font-size: 11px;
  flex-shrink: 0;
}
#palette-list li .cmd-ico svg { width: 14px; height: 14px; }
#palette-list li .cmd-name { color: var(--fg); }
#palette-list li .cmd-desc { font-size: 10.5px; color: var(--fg-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#palette-list li .right { margin-left: auto; display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
#palette-list li kbd {
  font-family: inherit;
  font-size: 10px;
  color: var(--fg-faint);
  border: 1px solid var(--border-soft);
  border-radius: 3px;
  padding: 1px 6px;
}
#palette-list li .tag-current {
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent-line);
  border-radius: 3px;
  padding: 1px 6px;
}
#palette-list li.active {
  background: var(--accent-soft);
}
#palette-list li.active .cmd-ico { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
#palette-list li.active .cmd-name { color: color-mix(in srgb, var(--fg) 92%, white); }
#palette-list li:hover:not(.active) { background: color-mix(in srgb, var(--fg) 4%, transparent); }

/* /help section divider — read-only label, not a selectable row */
#palette-list li.cmd-section {
  display: block;
  padding: 11px 8px 3px;
  font-size: 9px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--fg-faint);
  cursor: default;
  pointer-events: none;
}

#palette-foot {
  display: flex;
  gap: 14px;
  padding: 7px 13px;
  border-top: 1px solid var(--border-soft);
  font-size: 10px;
  color: var(--fg-faint);
}
#palette-foot b { color: var(--fg-dim); font-weight: 500; }

/* ── Share panel ───────────────────────────────────── */
#share-overlay {
  position: fixed;
  inset: 0;
  z-index: 110;
  background: color-mix(in srgb, var(--bg-page) 55%, transparent);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#share-overlay.hidden { display: none; }

#share-card {
  width: 520px;
  max-width: 100%;
  background: color-mix(in srgb, var(--bg-raised) 98%, transparent);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 30px 80px rgba(0,0,0,0.6), 0 0 0 1px var(--accent-soft);
  overflow: hidden;
  animation: share-in 0.2s var(--ease);
}
@keyframes share-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

.share-head {
  display: flex;
  align-items: center;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border-soft);
}
.share-head .t { font-size: 13px; color: color-mix(in srgb, var(--fg) 92%, white); font-weight: 500; }
.share-head .esc {
  margin-left: auto;
  font-size: 10px;
  color: var(--fg-faint);
  border: 1px solid var(--border-soft);
  border-radius: 3px;
  padding: 1px 6px;
  cursor: pointer;
}

.share-body { display: flex; gap: 20px; padding: 18px; }
#share-qr {
  width: 148px;
  height: 148px;
  flex-shrink: 0;
  background: #eef2ee;
  border-radius: 8px;
  padding: 10px;
  display: grid;
  place-items: center;
}
#share-qr img, #share-qr canvas { max-width: 100%; max-height: 100%; }
#share-qr .qr-too-big {
  font-size: 10px;
  color: #333;
  text-align: center;
  line-height: 1.5;
  padding: 6px;
}

.share-right { flex: 1; display: flex; flex-direction: column; min-width: 0; }
#share-link {
  font-size: 11px;
  color: var(--fg-dim);
  background: var(--bg-page);
  border: 1px solid var(--border-soft);
  border-radius: 6px;
  padding: 9px 11px;
  word-break: break-all;
  line-height: 1.5;
  max-height: 66px;
  overflow: hidden;
  position: relative;
}
#share-link .hl { color: var(--accent); }
#share-link::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 18px;
  background: linear-gradient(transparent, var(--bg-page));
}

.share-actions { display: flex; gap: 8px; margin-top: 10px; }
.btn {
  font-size: 12px;
  border-radius: 6px;
  padding: 7px 14px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.12s var(--ease), opacity 0.15s;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: none; opacity: 0.85; }
.btn.primary { background: var(--accent); color: var(--bg-page); font-weight: 600; }
.btn.ghost { background: transparent; color: var(--fg-dim); border-color: var(--border); }

/* Expiry selector — a ghost button that's a <select>, with a custom caret. */
#share-ttl {
  appearance: none;
  -webkit-appearance: none;
  font-family: inherit;
  padding-right: 28px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%236f7478' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
}
#share-ttl:hover { transform: none; color: var(--fg); }
#share-ttl:disabled { opacity: 0.4; cursor: not-allowed; }
#share-ttl option { background: var(--bg-raised); color: var(--fg); }

/* Full-screen state shown while opening / after an expired-or-missing tiny link. */
#tiny-state {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  background: var(--bg-page);
  color: var(--fg-dim);
  font-size: 13px;
  text-align: center;
  padding: 24px;
}
#tiny-state .ts-inner { display: grid; gap: 10px; }
#tiny-state a { color: var(--accent); text-decoration: none; font-size: 12px; }
#tiny-state a:hover { text-decoration: underline; }

.capacity { margin-top: auto; padding-top: 14px; }
.capacity .cap-row {
  display: flex;
  justify-content: space-between;
  font-size: 10.5px;
  color: var(--fg-faint);
  margin-bottom: 5px;
}
.capacity .cap-row b { color: var(--fg-dim); font-weight: 400; }
.capacity .cap-bar {
  height: 5px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  overflow: hidden;
  position: relative;
}
.capacity .cap-bar i {
  position: absolute;
  inset: 0;
  width: 0%;
  border-radius: 3px;
  background: var(--accent);
  transition: width 0.3s var(--ease);
}
#share-card.amber .cap-bar i { background: var(--warn); }
#share-card.red .cap-bar i   { background: var(--danger); }

.share-foot {
  padding: 10px 18px 13px;
  border-top: 1px solid var(--border-soft);
  font-size: 10.5px;
  color: var(--fg-faint);
  display: flex;
  align-items: center;
}
.share-foot .ico { color: var(--accent); }
.share-foot i { color: var(--fg-dim); font-style: normal; }

/* ── Focus mode ────────────────────────────────────── */
body.focus-mode .block:not(.active) {
  opacity: 0.16;
  filter: blur(0.6px);
}
body.focus-mode .block { transition: opacity 0.3s var(--ease), filter 0.3s var(--ease); }
body.focus-mode .block.active.text-block::before,
body.focus-mode .block.active.code-block::before {
  content: '';
  position: absolute;
  left: -2px;
  /* Was a fixed 10px inset, sized for the old 8px-padded block; a one-line block is
     now ~22px, which left a ~2px stub. A small proportional inset keeps the bar
     visible at one line and still short of the block's full height when it grows. */
  top: 3px;
  bottom: 3px;
  width: 2px;
  border-radius: 2px;
  background: var(--accent);
  box-shadow: 0 0 12px var(--accent-line);
  z-index: 3;
}
body.focus-mode .gutter { display: none; }

/* ── Floating command button (touch / small screens) ─ */
#fab {
  display: none;
  position: fixed;
  right: 16px;
  bottom: calc(var(--status-height) + 16px);
  width: 48px;
  height: 48px;
  border-radius: 14px;
  border: none;
  background: var(--accent);
  color: var(--bg-page);
  font-size: 20px;
  font-weight: 600;
  box-shadow: 0 10px 30px var(--accent-line);
  z-index: 90;
  cursor: pointer;
}
@media (hover: none), (max-width: 700px) {
  #fab { display: grid; place-items: center; }
}

/* ── Responsive ────────────────────────────────────── */
@media (max-width: 700px) {
  #document-container { padding: 20px 12px 24px; }

  .hint-cards { grid-template-columns: repeat(2, 1fr); }
  .wordmark { font-size: 24px; }
  .wordmark .cursor-blink { width: 11px; height: 21px; }

  #status { padding: 0 8px; font-size: 10.5px; }
  #status-lang, #status-font, #status-hint { display: none; }

  #palette {
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    border-radius: 16px 16px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
  }
  #palette::before {
    content: '';
    display: block;
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    margin: 8px auto 2px;
  }
  #palette.anchored { top: auto !important; left: 0 !important; bottom: 0; }
  #palette-list li { padding: 10px 8px; }
  #palette-foot { display: none; }

  .share-body { flex-direction: column; align-items: center; }
  .share-right { width: 100%; }
  .capacity { margin-top: 14px; }
}

/* ── Sidebar file tree ─────────────────────────────────────────────────── */
/* #document-container used to be body's flex:1 child directly; wrapping it in
   #app-shell moved that flex-grow responsibility here, or the whole shell
   collapses to content height instead of filling the space above #status. */
/* grid-template-rows is not decoration: the single implicit row would be `auto`, and
   it only stays capped at the flex-resolved height because both children happen to be
   scroll containers (which zeroes a grid item's automatic minimum size). The moment
   someone sets #sidebar { overflow: visible } — say, to hang a context menu off a row —
   the shell would overflow and the editor would grow under #status with no scrollbar.
   minmax(0, 1fr) makes that independent of the children. */
#app-shell {
  display: grid; grid-template-columns: var(--sb-width, 264px) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr); min-height: 0; flex: 1;
}
#app-shell.no-sidebar { grid-template-columns: 0 minmax(0, 1fr); }
#app-shell.no-sidebar #sidebar { display: none; }

/* Focus mode dims every inactive block to near-invisible; a 264px panel listing every
   note title beside it defeats the point. Hide it in CSS only, so the persisted `open`
   preference is untouched and the panel comes back on exit. */
body.focus-mode #app-shell { grid-template-columns: 0 minmax(0, 1fr); }
body.focus-mode #sidebar { display: none; }

/* Pin both children to explicit tracks: #sidebar going display:none removes it
   from grid flow, and without an explicit column #document-container's auto
   placement would slide into (and collapse to) the now-empty first track. */
/* Devicon-ish badge colours. These are file-type identity, not UI chrome — a JS
   file reads yellow in every editor — so they're fixed, like the wallpaper art.
   Declared on :root so every theme inherits them. */
:root {
  --sb-md: #89b4fa; --sb-js: #f7df1e; --sb-py: #4b8bbe; --sb-html: #e34c26;
  --sb-css: #cba6f7; --sb-json: #f9e2af; --sb-sh: #a6e3a1; --sb-sql: #74c7ec;
  --sb-java: #eba0ac; --sb-cpp: #89b4fa;
}
.sb-badge {
  width: 1.44em; flex: 0 0 1.44em; text-align: center;
  font-size: 0.76em; font-weight: 700; letter-spacing: -.3px;
}
.sb-open { margin-left: auto; padding-left: 8px; color: var(--accent); font-size: 0.72em; }

#sidebar  { grid-column: 1; }
#main-col {
  grid-column: 2; display: flex; flex-direction: column; min-width: 0; min-height: 0;
  /* isolation keeps the negative z-index layers below from escaping behind the page. */
  position: relative; isolation: isolate; overflow: hidden;
}

/* ── Backgrounds behind the note and the home screen ─────────────────────────
   Same three-layer recipe as #sidebar: artwork, scrim, then content on top. The
   filters live on the artwork layer alone, so no amount of blur ever touches the
   text you are reading.

   Deliberately on #main-col and not #document-container: that one scrolls, and an
   absolutely-positioned layer inside a scrolling box scrolls with the content —
   the image would slide out of view as you wrote. #tabline sits above with its own
   opaque background, so it covers the layer rather than floating on it. */
/* Two independent surfaces: the note area reads --nb-*, the home screen --hb-*.
   Filters live on the artwork layer alone, so no amount of blur touches the text. */
#main-col::before {
  content: ''; position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background-image: var(--nb-img, none);
  background-size: cover; background-position: var(--nb-pos, center);
  opacity: var(--nb-opacity, 0);
  filter: blur(var(--nb-blur, 3px)) brightness(var(--nb-bright, 0.65)) saturate(var(--nb-sat, 1));
}
#main-col::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, var(--bg-page), transparent 42%, var(--bg-page));
  opacity: var(--nb-scrim, 0);
}
#empty-state::before {
  content: ''; position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background-image: var(--hb-img, none);
  background-size: cover; background-position: var(--hb-pos, center);
  opacity: var(--hb-opacity, 0);
  filter: blur(var(--hb-blur, 2px)) brightness(var(--hb-bright, 0.85)) saturate(var(--hb-sat, 1));
}
#empty-state::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, var(--bg-page), transparent 42%, var(--bg-page));
  opacity: var(--hb-scrim, 0);
}
#main-col > *, #empty-state > * { position: relative; }
/* #document-container is a flex child again (of #main-col, under the tabline), so
   its flex:1 is live once more — unlike when it sat directly in the grid. */

/* ── Tabline (nvim-style, above the buffer) ─────────────────────────────── */
#tabline {
  display: flex; align-items: stretch; flex: 0 0 auto; height: calc(var(--ui-font-size) * 2.4);
  background: var(--bg-raised); border-bottom: 1px solid var(--accent-line);
  min-width: 0; font-size: var(--ui-font-size);
}
#tabline .tabs {
  display: flex; align-items: stretch; flex: 1; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
#tabline .tabs::-webkit-scrollbar { display: none; }
#tabline .tab {
  display: flex; align-items: center; gap: 7px; padding: 0 12px; flex: 0 0 auto;
  border: 0; border-right: 1px solid var(--bg-page); cursor: pointer;
  background: var(--bg-code); color: var(--fg-dim);
  font: inherit; white-space: nowrap; max-width: 220px;
}
#tabline .tab:hover { color: var(--fg); }
#tabline .tab.active { background: var(--accent); color: var(--bg-page); font-weight: 600; }
#tabline .tab .t-idx  { opacity: .7; font-size: 0.88em; }
#tabline .tab .t-name { overflow: hidden; text-overflow: ellipsis; }
#tabline .tab .t-dirty { color: var(--accent); font-size: 1.12em; line-height: 1; }
#tabline .tab.active .t-dirty { color: var(--bg-page); }
/* ✎ and × share one treatment: invisible until the tab is hovered, so the tabline
   stays quiet, then lit individually on their own hover. Grouped in one selector
   because they must stay identical — a rename that looked different from close
   would read as a different KIND of control rather than a sibling of it. */
#tabline .tab .t-x,
#tabline .tab .t-ren { opacity: 0; padding: 0 2px; border-radius: 3px; font-size: 1.12em; line-height: 1; }
#tabline .tab .t-ren { font-size: .95em; }   /* ✎ carries more ink than × at a size */
#tabline .tab:hover .t-x,
#tabline .tab:hover .t-ren { opacity: .55; }
#tabline .tab .t-x:hover,
#tabline .tab .t-ren:hover { opacity: 1; background: rgba(0,0,0,.25); }
#tabline #tab-new, #tabline #tab-help {
  flex: 0 0 auto; border: 0; padding: 0 12px; cursor: pointer; font: inherit;
  background: var(--bg-raised); color: var(--fg-dim); border-left: 1px solid var(--bg-page);
}
#tabline #tab-new { color: var(--accent); font-size: 1.2em; }
#tabline #tab-help:hover, #tabline #tab-new:hover { color: var(--fg); }
body.focus-mode #tabline { display: none; }

#sidebar {
  position: relative; isolation: isolate; display: flex; flex-direction: column;
  min-width: 0; overflow: hidden;
  /* The scaling root: every size inside is an em fraction of this. */
  font-size: var(--ui-font-size);
  background: var(--bg-raised); border-right: 1px solid var(--accent-line);
}
/* Artwork and scrim are separate layers so blur()/brightness() never touch the
   file names, which sit above both. */
#sidebar::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: var(--sb-img, none);
  background-size: cover; background-position: var(--sb-pos, center);
  opacity: var(--sb-opacity, 0.45);
  filter: blur(var(--sb-blur, 2px)) brightness(var(--sb-bright, 1)) saturate(var(--sb-sat, 1.1));
}
#sidebar::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, var(--bg-raised), transparent 45%, var(--bg-raised));
  opacity: var(--sb-scrim, 0.55);
}
#sidebar > * { position: relative; }

/* ── Resize handle ───────────────────────────────────────────────────────────
   Sits on the panel's right edge. Deliberately wider than it looks (8px of target
   for a 1px line) — a hairline border is a miserable thing to hit with a mouse. */
#sb-resize {
  position: absolute; top: 0; right: 0; bottom: 0; width: 8px;
  cursor: col-resize; z-index: 3; background: none; border: 0;
  /* The visible line is a child pseudo-element so the hit area can stay generous
     without painting an 8px stripe over the wallpaper. */
}
#sb-resize::after {
  content: ''; position: absolute; top: 0; bottom: 0; right: 0; width: 2px;
  background: var(--accent); opacity: 0; transition: opacity .12s var(--ease);
}
#sb-resize:hover::after, #sb-resize:focus-visible::after, body.sb-resizing #sb-resize::after {
  opacity: .8;
}
#sb-resize:focus-visible { outline: none; }
/* While dragging, the pointer is captured — stop the editor selecting text under it
   and keep the resize cursor even when the pointer outruns the handle. */
body.sb-resizing { cursor: col-resize; user-select: none; }
body.sb-resizing #document-container { pointer-events: none; }

.sb-head { display: flex; align-items: center; gap: 6px; padding: 10px 12px 8px; }
.sb-tilde { color: var(--accent); }
.sb-path { color: var(--fg-dim); font-size: 0.96em; flex: 1; }
#sb-new { border: 0; background: none; color: var(--accent); cursor: pointer; font: inherit; font-size: 1.28em; padding: 0 4px; }
#sb-new:hover { color: var(--fg); }

#sidebar-tree { flex: 1; overflow: auto; padding-bottom: 8px; }
.sb-row {
  display: flex; align-items: center; gap: 7px; width: 100%;
  /* Row height tracks the text, or shrinking the font just leaves taller gaps. */
  height: calc(var(--ui-font-size) * 1.92);
  padding: 0 10px 0 0; border: 0; background: none; cursor: pointer;
  color: var(--fg-dim); font: inherit; font-size: 1em; text-align: left; white-space: nowrap;
}
.sb-row:hover { background: var(--accent-soft); color: var(--fg); }
.sb-row.dir { color: var(--accent); }
.sb-row .sb-chev { width: 1.12em; flex: 0 0 1.12em; text-align: center; color: var(--fg-faint); font-size: 0.8em; }
.sb-row .sb-name { overflow: hidden; text-overflow: ellipsis; }
.sb-row .sb-count { margin-left: auto; color: var(--fg-faint); font-size: 0.88em; }
.sb-foot { padding: 7px 12px; border-top: 1px solid var(--accent-line); color: var(--fg-faint); font-size: 0.88em; }

@media (max-width: 820px) {
  #app-shell { grid-template-columns: 0 minmax(0, 1fr); }
  #sidebar { display: none; }
}

/* ── Line-number gutter (vim style) ────────────────────────────────────────
   Numbers live inside each block so they inherit its font and line-height and
   stay aligned; the tail fills the rest of the viewport with tildes. */
.blk-lines {
  /* top:0 now lands on the first line because .block-content no longer has vertical
     padding. font-size stays small for legibility, but the row height is locked to
     the content's computed line box — at 13px/1.6 the numbers drifted 1.6px per line
     against 14px/1.6 text, so by line 20 they were a full row out (issue #30). */
  position: absolute; left: -46px; top: 0; width: 34px;
  display: flex; flex-direction: column; align-items: flex-end;
  color: var(--fg-faint); font-size: 12.5px; text-align: right;
  line-height: calc(var(--font-size) * var(--line-height));
  user-select: none; pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.blk-lines span { line-height: inherit; height: calc(var(--font-size) * var(--line-height)); }
.block.active .blk-lines span:first-child { color: var(--accent); }

/* ── Settings float: wallpaper swatches ────────────────────────────────────
   The mock picked backgrounds by sight, not by reading a list of names. */
.pal-sect {
  color: var(--accent); font-size: 11px; letter-spacing: .6px;
  margin: 10px 0 8px; text-transform: lowercase;
}
/* Which surface the picker below is driving. A segmented control rather than two
   sections, so the twenty-swatch grid is drawn once instead of once per surface. */
.pal-target { display: flex; gap: 0; margin: 0 0 8px; border: 1px solid var(--border); border-radius: 5px; overflow: hidden; }
.pal-tgt {
  flex: 1; padding: 5px 8px; border: 0; background: none; cursor: pointer;
  color: var(--fg-dim); font: inherit; font-size: 11px; transition: background .12s var(--ease), color .12s var(--ease);
}
.pal-tgt:hover { color: var(--fg); background: var(--accent-soft); }
.pal-tgt.on { background: var(--accent); color: var(--bg-page); font-weight: 600; }
.pal-tgt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.pal-swatches {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 9px; margin-bottom: 4px;
}
.pal-swatch {
  position: relative; height: 62px; padding: 0; cursor: pointer;
  border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
  /* The presets are mostly translucent gradients, so they need the panel's own
     colour underneath — without it a button's default UA background shows through
     and every swatch reads as washed-out white. */
  background-color: var(--bg-raised);
  background-size: cover; background-position: center;
}
.pal-swatch:hover { border-color: var(--fg-dim); }
.pal-swatch.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.pal-swatch span {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 3px 6px;
  text-align: left; font-size: 10.5px; color: #fff;
  background: linear-gradient(transparent, rgba(0,0,0,.85));
}
.pal-swatch.on span::after { content: ' ✓'; color: var(--accent); }
body.focus-mode .blk-lines { display: none; }
@media (max-width: 820px) { .blk-lines { display: none; } }

/* ── Sidebar row actions (rename / delete / new subfolder) ───────────────────
   Hidden until hover or keyboard focus so the tree stays quiet, but always
   reachable — the panel previously had no way to delete a note at all. */
.sb-act {
  /* Visible at rest, not hidden until hover. These were opacity:0, so the only way to
     discover that a row could be renamed at all was to hover it and notice a faint
     glyph appear — people reasonably concluded the feature did not exist. */
  opacity: .45; flex: 0 0 auto; padding: 1px 4px; margin-left: 2px;
  color: var(--fg-dim); font-size: 1em; line-height: 1; border-radius: 3px;
  transition: opacity .12s var(--ease), color .12s var(--ease);
}
.sb-row:hover .sb-act, .sb-row:focus-within .sb-act { opacity: .9; }
.sb-act:hover { opacity: 1; color: var(--fg); background: var(--accent-soft); }
.sb-act.sb-del:hover { opacity: 1; color: var(--danger); background: color-mix(in srgb, var(--danger) 15%, transparent); }
/* ── Row trailing columns ────────────────────────────────────────────────────
   The name grows to fill, so everything after it is pinned to the right edge
   instead of trailing wherever the text happens to stop. It was `flex: 0 1 auto`
   — shrink but never grow — which left the rename and delete icons sitting at a
   different x on every row, following each file name like punctuation.

   The three trailing slots are fixed width and always present (the open-note dot
   renders empty when the note isn't open), so ✎ and ✕ line up down a column
   across notes and folders alike. */
.sb-row .sb-name { flex: 1 1 auto; min-width: 0; }
.sb-row .sb-count,
.sb-row .sb-open {
  /* min-width, not width: a folder count is unbounded, and a fixed box clipped "12"
     to "1" — the same class of misalignment this block exists to fix, one column
     over. The slot still reserves its space when the number is a single digit. */
  flex: 0 0 auto; min-width: 1.1em; margin-left: 4px; text-align: center;
}
.sb-act { flex: 0 0 auto; width: 1.5em; text-align: center; padding: 1px 0; margin-left: 2px; }

/* ── Settings float: give the list and the swatches their own room ───────────
   #palette-list was height-capped while the swatches and bars took the rest, so
   only one row ("hide sidebar") was visible and "reset background" was cut off. */
#palette.float #palette-list { flex: 0 0 auto; max-height: 30vh; overflow-y: auto; }
#palette.float #palette-bars { flex: 1 1 auto; overflow-y: auto; min-height: 0; }

/* Focus has to out-shout the artwork. A 2px ring at 2px offset sat directly against
   the photo and vanished on the bright ones, and it read almost identically to the
   `.on` selected border — so "where I am" and "what is chosen" looked the same.
   The inner ring of panel colour separates the accent from whatever is behind it,
   which is what makes this legible over every wallpaper rather than most of them. */
.pal-swatch:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--bg-raised);
}
.pal-upload {
  display: grid; place-items: center;
  background-image: none; background: var(--bg-code);
  border-style: dashed; color: var(--fg-dim); font-size: 12px;
}
.pal-upload:hover { color: var(--accent); border-color: var(--accent); }
.pal-upload span { position: static; background: none; padding: 0; }

/* ── Folder glyph in the tree ────────────────────────────────────────────────
   The mock led every folder with an nvim-tree folder icon; a bare chevron made
   folders read as just another indented row. Filled when open, outlined when
   collapsed, so the state is legible without reading the chevron. */
.sb-folder {
  /* em, like everything else in .sb-row — a fixed icon next to text that scales
     from 9px to 20px goes from dwarfing the name to disappearing beside it. */
  width: 1.2em; height: 1.2em; flex: 0 0 1.2em;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--accent);
}
.sb-folder svg { width: 1.12em; height: 1.12em; display: block; }

/* ── /help as a reference sheet, not a command list ──────────────────────────
   Keycaps lead each row and share a column, so the eye scans keys down the left
   and meanings down the right — the mock's layout. */
#palette-list.help-grid .help-row {
  display: flex; align-items: baseline; gap: 14px;
  padding: 5px 13px; cursor: default; background: none;
}
#palette-list.help-grid .help-keys {
  flex: 0 0 132px; display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-start;
}
#palette-list.help-grid .help-keys kbd {
  background: var(--bg-code); color: var(--accent);
  border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 4px;
  padding: 1px 6px; font: inherit; font-size: 11px; line-height: 1.5;
}
#palette-list.help-grid .help-text { color: var(--fg-dim); font-size: 12.5px; }
/* Read-only: no row highlight to imply something will happen on Enter. */
#palette-list.help-grid .help-row:hover { background: none; }

/* ── Drag a note onto a folder ───────────────────────────────────────────── */
.sb-row[draggable="true"] { cursor: grab; }
.sb-row.dragging { opacity: .45; }
/* The drop target is the folder itself, so highlight the row rather than a line
   between rows — this reorders nothing, it only re-files. */
.sb-row.dir.drop-into {
  background: var(--accent-soft);
  box-shadow: inset 2px 0 var(--accent);
}
/* A help row with no key of its own spans the full width instead of leaving an
   empty keycap column beside it. */
#palette-list.help-grid .help-row.help-prose { padding-left: 13px; }
#palette-list.help-grid .help-row.help-prose .help-text { color: var(--fg); }

/* ── Sync indicator ──────────────────────────────────────────────────────────
   Persistent, because the thing it reports is persistent. A failed push used to be
   a toast that cleared after 1.5s, and from then on a dead connection was
   indistinguishable from a working one — the app kept taking edits and quietly kept
   them to itself. Three states, told apart by colour rather than by reading:
   off is quiet, on is quiet, and only "not getting through" asks for attention. */
#status .seg.sync { cursor: default; white-space: nowrap; }
#status .seg.sync .sync-short { display: none; }
#status .seg.sync[data-tone="off"]  { color: var(--fg-faint); }
#status .seg.sync[data-tone="ok"]   { color: var(--fg-dim); }
#status .seg.sync[data-tone="warn"] {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  font-weight: 500;
}

/* Narrow screens. This block must stay BELOW the base .sync rules above: a media
   query adds no specificity, so whichever of two single-class rules comes last in the
   file wins, and putting this higher up silently hid both spans at once. */
@media (max-width: 700px) {
  /* The segment itself stays, unlike its neighbours. It is the only thing in this bar
     that reports a problem, and a phone is where a flaky connection is likeliest. */
  #status .seg.sync .sync-full  { display: none; }
  #status .seg.sync .sync-short { display: inline; }
}
