← Notes results

Notes

Grok 4.5 · legacy

Initial view of Notes

Browser check: Artifact runtime error. This is a technical check, not a quality review.

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 83,886 bytes · SHA-256 90a808669c6b
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lumen Notes</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<style>
  :root {
    --bg: #f6f5f2;
    --bg-elevated: #ffffff;
    --bg-sidebar: #efeee9;
    --bg-hover: rgba(0,0,0,0.05);
    --bg-active: rgba(0,0,0,0.08);
    --bg-input: #ffffff;
    --border: rgba(0,0,0,0.08);
    --border-strong: rgba(0,0,0,0.12);
    --text: #1a1a1a;
    --text-secondary: #6b6b6b;
    --text-muted: #9a9a9a;
    --accent: #5b6cfa;
    --accent-soft: rgba(91,108,250,0.12);
    --accent-hover: #4a5ae8;
    --danger: #e05454;
    --danger-soft: rgba(224,84,84,0.12);
    --success: #3d9a6a;
    --warning: #d4a017;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
    --shadow: 0 4px 16px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
    --shadow-lg: 0 12px 40px rgba(0,0,0,0.1), 0 4px 12px rgba(0,0,0,0.05);
    --radius: 10px;
    --radius-sm: 6px;
    --radius-lg: 14px;
    --sidebar-w: 280px;
    --list-w: 300px;
    --font: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
    --font-mono: 'JetBrains Mono', ui-monospace, monospace;
    --transition: 160ms cubic-bezier(0.4, 0, 0.2, 1);
  }

  [data-theme="dark"] {
    --bg: #141416;
    --bg-elevated: #1c1c1f;
    --bg-sidebar: #18181b;
    --bg-hover: rgba(255,255,255,0.05);
    --bg-active: rgba(255,255,255,0.08);
    --bg-input: #252528;
    --border: rgba(255,255,255,0.08);
    --border-strong: rgba(255,255,255,0.12);
    --text: #f0f0f2;
    --text-secondary: #a0a0a8;
    --text-muted: #6a6a72;
    --accent: #7b8cff;
    --accent-soft: rgba(123,140,255,0.15);
    --accent-hover: #8f9eff;
    --danger: #f07070;
    --danger-soft: rgba(240,112,112,0.15);
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
    --shadow: 0 4px 16px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.2);
    --shadow-lg: 0 12px 40px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.25);
  }

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

  html, body {
    height: 100%;
    overflow: hidden;
    font-family: var(--font);
    font-size: 14px;
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }

  button, input, textarea { font-family: inherit; font-size: inherit; color: inherit; }
  button { cursor: pointer; border: none; background: none; }
  ::selection { background: var(--accent-soft); color: var(--text); }

  /* ─── App Shell ─── */
  .app {
    display: grid;
    grid-template-columns: var(--sidebar-w) var(--list-w) 1fr;
    height: 100%;
    transition: grid-template-columns var(--transition);
  }
  .app.sidebar-collapsed { grid-template-columns: 0 var(--list-w) 1fr; }
  .app.list-collapsed { grid-template-columns: var(--sidebar-w) 0 1fr; }
  .app.sidebar-collapsed.list-collapsed { grid-template-columns: 0 0 1fr; }

  /* ─── Sidebar ─── */
  .sidebar {
    background: var(--bg-sidebar);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: opacity var(--transition);
  }
  .app.sidebar-collapsed .sidebar { opacity: 0; pointer-events: none; }

  .sidebar-header {
    padding: 16px 14px 12px;
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .logo {
    display: flex;
    align-items: center;
    gap: 9px;
    flex: 1;
    min-width: 0;
  }
  .logo-mark {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: linear-gradient(135deg, var(--accent), #a78bfa);
    display: grid;
    place-items: center;
    color: white;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(91,108,250,0.35);
  }
  .logo-mark svg { width: 15px; height: 15px; }
  .logo-text {
    font-weight: 700;
    font-size: 15px;
    letter-spacing: -0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .icon-btn {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    display: grid;
    place-items: center;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition);
    flex-shrink: 0;
  }
  .icon-btn:hover { background: var(--bg-hover); color: var(--text); }
  .icon-btn:active { background: var(--bg-active); }
  .icon-btn svg { width: 16px; height: 16px; stroke-width: 1.75; }
  .icon-btn.sm { width: 28px; height: 28px; }
  .icon-btn.sm svg { width: 14px; height: 14px; }
  .icon-btn.danger:hover { background: var(--danger-soft); color: var(--danger); }

  .search-wrap {
    padding: 0 12px 12px;
  }
  .search-box {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0 10px;
    height: 36px;
    transition: border-color var(--transition), box-shadow var(--transition);
  }
  .search-box:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .search-box svg { width: 15px; height: 15px; color: var(--text-muted); flex-shrink: 0; }
  .search-box input {
    flex: 1;
    border: none;
    outline: none;
    background: transparent;
    min-width: 0;
  }
  .search-box input::placeholder { color: var(--text-muted); }
  .search-box .clear-search {
    display: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--bg-hover);
    color: var(--text-secondary);
    place-items: center;
  }
  .search-box .clear-search.visible { display: grid; }
  .search-box .clear-search svg { width: 10px; height: 10px; }

  .nav-section {
    padding: 4px 10px 8px;
  }
  .nav-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    padding: 8px 8px 6px;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .nav-item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition);
    user-select: none;
    width: 100%;
    text-align: left;
  }
  .nav-item:hover { background: var(--bg-hover); color: var(--text); }
  .nav-item.active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 500;
  }
  .nav-item svg { width: 16px; height: 16px; flex-shrink: 0; stroke-width: 1.75; }
  .nav-item .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nav-item .count {
    font-size: 11px;
    font-weight: 500;
    color: var(--text-muted);
    background: var(--bg-hover);
    padding: 1px 6px;
    border-radius: 10px;
    min-width: 20px;
    text-align: center;
  }
  .nav-item.active .count { background: var(--accent-soft); color: var(--accent); }

  .notebook-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
  }

  .nav-item .item-actions {
    display: none;
    gap: 2px;
  }
  .nav-item:hover .item-actions { display: flex; }
  .nav-item:hover .count { display: none; }

  .sidebar-scroll {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
  }

  .sidebar-footer {
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    display: flex;
    gap: 4px;
  }

  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    height: 34px;
    padding: 0 14px;
    border-radius: var(--radius-sm);
    font-weight: 500;
    font-size: 13px;
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
    white-space: nowrap;
  }
  .btn svg { width: 15px; height: 15px; stroke-width: 1.75; }
  .btn-primary {
    background: var(--accent);
    color: white;
  }
  .btn-primary:hover { background: var(--accent-hover); }
  .btn-primary:active { transform: scale(0.98); }
  .btn-ghost {
    background: transparent;
    color: var(--text-secondary);
  }
  .btn-ghost:hover { background: var(--bg-hover); color: var(--text); }
  .btn-soft {
    background: var(--accent-soft);
    color: var(--accent);
  }
  .btn-soft:hover { background: var(--accent); color: white; }
  .btn-block { width: 100%; }
  .btn-danger {
    background: var(--danger-soft);
    color: var(--danger);
  }
  .btn-danger:hover { background: var(--danger); color: white; }

  /* ─── Note List ─── */
  .note-list-panel {
    background: var(--bg-elevated);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: opacity var(--transition);
  }
  .app.list-collapsed .note-list-panel { opacity: 0; pointer-events: none; }

  .list-header {
    padding: 14px 14px 10px;
    display: flex;
    align-items: center;
    gap: 8px;
    border-bottom: 1px solid var(--border);
    min-height: 56px;
  }
  .list-header h2 {
    flex: 1;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.02em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .list-meta {
    padding: 0 14px 10px;
    font-size: 12px;
    color: var(--text-muted);
  }

  .sort-row {
    padding: 0 10px 8px;
    display: flex;
    gap: 4px;
  }
  .chip {
    height: 26px;
    padding: 0 10px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-secondary);
    background: transparent;
    transition: background var(--transition), color var(--transition);
  }
  .chip:hover { background: var(--bg-hover); color: var(--text); }
  .chip.active { background: var(--accent-soft); color: var(--accent); }

  .notes-scroll {
    flex: 1;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
    padding: 4px 8px 16px;
  }

  .note-card {
    padding: 12px 12px;
    border-radius: var(--radius);
    cursor: pointer;
    transition: background var(--transition);
    border: 1px solid transparent;
    margin-bottom: 2px;
    position: relative;
  }
  .note-card:hover { background: var(--bg-hover); }
  .note-card.active {
    background: var(--accent-soft);
    border-color: transparent;
  }
  .note-card-top {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-bottom: 4px;
  }
  .note-card-title {
    flex: 1;
    font-weight: 600;
    font-size: 13.5px;
    letter-spacing: -0.01em;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .note-card .pin-icon {
    color: var(--warning);
    flex-shrink: 0;
    margin-top: 1px;
  }
  .note-card .pin-icon svg { width: 12px; height: 12px; fill: currentColor; }
  .note-card-preview {
    font-size: 12.5px;
    color: var(--text-secondary);
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    margin-bottom: 8px;
  }
  .note-card-footer {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
  }
  .note-card-date {
    font-size: 11px;
    color: var(--text-muted);
  }
  .tag {
    font-size: 11px;
    font-weight: 500;
    padding: 1px 7px;
    border-radius: 4px;
    background: var(--bg-hover);
    color: var(--text-secondary);
  }
  .note-card.active .tag { background: rgba(91,108,250,0.1); color: var(--accent); }

  .empty-list {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    text-align: center;
    color: var(--text-muted);
    gap: 8px;
  }
  .empty-list svg { width: 40px; height: 40px; opacity: 0.4; margin-bottom: 4px; }
  .empty-list p { font-size: 13px; line-height: 1.5; max-width: 180px; }

  /* ─── Editor ─── */
  .editor-panel {
    background: var(--bg-elevated);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
  }

  .editor-toolbar {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--border);
    min-height: 52px;
    flex-wrap: wrap;
  }
  .editor-toolbar .spacer { flex: 1; }
  .toolbar-group {
    display: flex;
    align-items: center;
    gap: 1px;
    padding: 0 4px;
  }
  .toolbar-group + .toolbar-group {
    border-left: 1px solid var(--border);
    padding-left: 8px;
    margin-left: 4px;
  }
  .tb-btn {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    display: grid;
    place-items: center;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition);
  }
  .tb-btn:hover { background: var(--bg-hover); color: var(--text); }
  .tb-btn.active { background: var(--accent-soft); color: var(--accent); }
  .tb-btn svg { width: 15px; height: 15px; stroke-width: 1.75; }
  .tb-btn[data-cmd="bold"] { font-weight: 700; font-size: 14px; }
  .tb-btn[data-cmd="italic"] { font-style: italic; font-family: Georgia, serif; font-size: 15px; }
  .tb-sep {
    width: 1px;
    height: 18px;
    background: var(--border);
    margin: 0 6px;
  }

  .editor-body {
    flex: 1;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
  }

  .editor-inner {
    max-width: 720px;
    margin: 0 auto;
    padding: 36px 48px 120px;
  }

  .editor-meta-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
    flex-wrap: wrap;
  }
  .editor-meta-bar .meta-date {
    font-size: 12px;
    color: var(--text-muted);
  }
  .editor-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
  }
  .editor-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 500;
    padding: 3px 8px;
    border-radius: 5px;
    background: var(--accent-soft);
    color: var(--accent);
    cursor: default;
  }
  .editor-tag button {
    display: grid;
    place-items: center;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    color: var(--accent);
    opacity: 0.6;
  }
  .editor-tag button:hover { opacity: 1; background: rgba(91,108,250,0.15); }
  .editor-tag button svg { width: 9px; height: 9px; }
  .add-tag-btn {
    font-size: 12px;
    color: var(--text-muted);
    padding: 3px 8px;
    border-radius: 5px;
    border: 1px dashed var(--border-strong);
    transition: color var(--transition), border-color var(--transition);
  }
  .add-tag-btn:hover { color: var(--accent); border-color: var(--accent); }

  .note-title {
    width: 100%;
    border: none;
    outline: none;
    background: transparent;
    font-size: 32px;
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.25;
    color: var(--text);
    margin-bottom: 8px;
    resize: none;
    overflow: hidden;
    min-height: 40px;
  }
  .note-title::placeholder { color: var(--text-muted); }

  .note-content {
    outline: none;
    min-height: 300px;
    font-size: 15.5px;
    line-height: 1.7;
    color: var(--text);
    letter-spacing: -0.01em;
  }
  .note-content:empty::before {
    content: attr(data-placeholder);
    color: var(--text-muted);
    pointer-events: none;
  }
  .note-content h1 { font-size: 1.6em; font-weight: 700; margin: 1.2em 0 0.4em; letter-spacing: -0.02em; }
  .note-content h2 { font-size: 1.3em; font-weight: 650; margin: 1.1em 0 0.35em; letter-spacing: -0.02em; }
  .note-content h3 { font-size: 1.1em; font-weight: 600; margin: 1em 0 0.3em; }
  .note-content p { margin: 0.4em 0; }
  .note-content ul, .note-content ol { padding-left: 1.5em; margin: 0.5em 0; }
  .note-content li { margin: 0.25em 0; }
  .note-content blockquote {
    border-left: 3px solid var(--accent);
    padding: 4px 16px;
    margin: 0.8em 0;
    color: var(--text-secondary);
    background: var(--accent-soft);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  }
  .note-content code {
    font-family: var(--font-mono);
    font-size: 0.88em;
    background: var(--bg-hover);
    padding: 2px 6px;
    border-radius: 4px;
  }
  .note-content pre {
    font-family: var(--font-mono);
    font-size: 0.88em;
    background: var(--bg-sidebar);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    overflow-x: auto;
    margin: 0.8em 0;
    line-height: 1.55;
  }
  .note-content pre code { background: none; padding: 0; }
  .note-content a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
  .note-content hr {
    border: none;
    border-top: 1px solid var(--border-strong);
    margin: 1.5em 0;
  }
  .note-content img { max-width: 100%; border-radius: var(--radius); margin: 0.5em 0; }

  .editor-empty {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    gap: 12px;
    padding: 40px;
    text-align: center;
  }
  .editor-empty .empty-icon {
    width: 72px;
    height: 72px;
    border-radius: 20px;
    background: var(--bg-sidebar);
    display: grid;
    place-items: center;
    margin-bottom: 8px;
  }
  .editor-empty .empty-icon svg { width: 32px; height: 32px; opacity: 0.5; }
  .editor-empty h3 { font-size: 17px; font-weight: 600; color: var(--text-secondary); letter-spacing: -0.02em; }
  .editor-empty p { font-size: 13.5px; max-width: 280px; line-height: 1.55; }
  .editor-empty .shortcuts {
    display: flex;
    gap: 16px;
    margin-top: 20px;
    flex-wrap: wrap;
    justify-content: center;
  }
  .shortcut {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-muted);
  }
  kbd {
    font-family: var(--font);
    font-size: 11px;
    font-weight: 500;
    background: var(--bg-sidebar);
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    padding: 2px 6px;
    color: var(--text-secondary);
    box-shadow: 0 1px 0 var(--border-strong);
  }

  /* ─── Modals ─── */
  .modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms ease;
  }
  .modal-overlay.open { opacity: 1; pointer-events: auto; }
  .modal {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: min(400px, calc(100vw - 32px));
    padding: 24px;
    transform: scale(0.96) translateY(8px);
    transition: transform 200ms cubic-bezier(0.34, 1.2, 0.64, 1);
  }
  .modal-overlay.open .modal { transform: scale(1) translateY(0); }
  .modal h3 {
    font-size: 17px;
    font-weight: 650;
    letter-spacing: -0.02em;
    margin-bottom: 6px;
  }
  .modal .modal-desc {
    font-size: 13px;
    color: var(--text-secondary);
    margin-bottom: 18px;
    line-height: 1.5;
  }
  .modal-field { margin-bottom: 14px; }
  .modal-field label {
    display: block;
    font-size: 12px;
    font-weight: 550;
    color: var(--text-secondary);
    margin-bottom: 6px;
  }
  .modal-field input, .modal-field select {
    width: 100%;
    height: 38px;
    padding: 0 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--bg-input);
    outline: none;
    transition: border-color var(--transition), box-shadow var(--transition);
  }
  .modal-field input:focus, .modal-field select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }
  .color-picks {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
  }
  .color-pick {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2px solid transparent;
    transition: border-color var(--transition), transform var(--transition);
    cursor: pointer;
  }
  .color-pick:hover { transform: scale(1.1); }
  .color-pick.selected { border-color: var(--text); box-shadow: 0 0 0 2px var(--bg-elevated); }

  .modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 20px;
  }

  /* Command palette */
  .cmd-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.35);
    backdrop-filter: blur(3px);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 15vh;
    z-index: 1100;
    opacity: 0;
    pointer-events: none;
    transition: opacity 150ms ease;
  }
  .cmd-overlay.open { opacity: 1; pointer-events: auto; }
  .cmd-palette {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: min(520px, calc(100vw - 32px));
    overflow: hidden;
    transform: scale(0.97) translateY(-6px);
    transition: transform 180ms cubic-bezier(0.34, 1.2, 0.64, 1);
  }
  .cmd-overlay.open .cmd-palette { transform: scale(1) translateY(0); }
  .cmd-input-wrap {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
  }
  .cmd-input-wrap svg { width: 18px; height: 18px; color: var(--text-muted); flex-shrink: 0; }
  .cmd-input-wrap input {
    flex: 1;
    border: none;
    outline: none;
    background: transparent;
    font-size: 15px;
  }
  .cmd-input-wrap input::placeholder { color: var(--text-muted); }
  .cmd-results {
    max-height: 320px;
    overflow-y: auto;
    padding: 6px;
  }
  .cmd-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--transition);
  }
  .cmd-item:hover, .cmd-item.highlighted { background: var(--bg-hover); }
  .cmd-item .cmd-icon {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    background: var(--bg-sidebar);
    display: grid;
    place-items: center;
    color: var(--text-secondary);
    flex-shrink: 0;
  }
  .cmd-item .cmd-icon svg { width: 14px; height: 14px; }
  .cmd-item .cmd-label { flex: 1; font-weight: 500; font-size: 13.5px; }
  .cmd-item .cmd-hint { font-size: 11px; color: var(--text-muted); }
  .cmd-empty {
    padding: 24px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
  }

  /* Toast */
  .toasts {
    position: fixed;
    bottom: 24px;
    right: 24px;
    z-index: 2000;
    display: flex;
    flex-direction: column-reverse;
    gap: 8px;
  }
  .toast {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 12px 16px;
    font-size: 13px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 10px;
    animation: toastIn 250ms cubic-bezier(0.34, 1.2, 0.64, 1);
    min-width: 200px;
  }
  .toast.out { animation: toastOut 200ms ease forwards; }
  @keyframes toastIn {
    from { opacity: 0; transform: translateY(12px) scale(0.96); }
    to { opacity: 1; transform: none; }
  }
  @keyframes toastOut {
    to { opacity: 0; transform: translateY(8px) scale(0.96); }
  }
  .toast svg { width: 16px; height: 16px; flex-shrink: 0; }
  .toast.success svg { color: var(--success); }
  .toast.info svg { color: var(--accent); }

  /* Context menu */
  .context-menu {
    position: fixed;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 4px;
    z-index: 1500;
    min-width: 180px;
    display: none;
  }
  .context-menu.open { display: block; animation: toastIn 120ms ease; }
  .ctx-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--text);
    transition: background var(--transition);
    text-align: left;
  }
  .ctx-item:hover { background: var(--bg-hover); }
  .ctx-item.danger { color: var(--danger); }
  .ctx-item.danger:hover { background: var(--danger-soft); }
  .ctx-item svg { width: 14px; height: 14px; opacity: 0.7; }
  .ctx-sep { height: 1px; background: var(--border); margin: 4px 6px; }

  /* Status bar */
  .status-bar {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 28px;
    display: flex;
    align-items: center;
    padding: 0 16px;
    font-size: 11px;
    color: var(--text-muted);
    border-top: 1px solid var(--border);
    background: var(--bg-elevated);
    gap: 16px;
  }
  .status-bar .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--success);
  }
  .status-bar .stat-right { margin-left: auto; display: flex; gap: 16px; }

  /* Responsive */
  @media (max-width: 900px) {
    .app { grid-template-columns: 0 var(--list-w) 1fr; }
    .sidebar { position: fixed; left: 0; top: 0; bottom: 0; width: var(--sidebar-w); z-index: 100; box-shadow: var(--shadow-lg); transform: translateX(-100%); transition: transform 200ms ease; }
    .app.mobile-sidebar .sidebar { transform: translateX(0); opacity: 1; pointer-events: auto; }
    .app.sidebar-collapsed { grid-template-columns: 0 var(--list-w) 1fr; }
  }
  @media (max-width: 640px) {
    .app, .app.sidebar-collapsed, .app.list-collapsed { grid-template-columns: 1fr; }
    .note-list-panel { position: fixed; inset: 0; z-index: 50; width: 100%; transition: transform 200ms ease; }
    .app.mobile-editor .note-list-panel { transform: translateX(-100%); }
    .editor-inner { padding: 24px 20px 100px; }
    .note-title { font-size: 24px; }
  }

  /* Focus mode */
  .app.focus-mode { grid-template-columns: 0 0 1fr; }
  .app.focus-mode .sidebar,
  .app.focus-mode .note-list-panel { opacity: 0; pointer-events: none; }
</style>
</head>
<body>
<div class="app" id="app">
  <!-- Sidebar -->
  <aside class="sidebar" id="sidebar">
    <div class="sidebar-header">
      <div class="logo">
        <div class="logo-mark">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
        </div>
        <span class="logo-text">Lumen Notes</span>
      </div>
      <button class="icon-btn" id="btnTheme" title="Toggle theme">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>
      </button>
    </div>

    <div class="search-wrap">
      <div class="search-box">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
        <input type="text" id="searchInput" placeholder="Search notes…" autocomplete="off" />
        <button class="clear-search" id="clearSearch" title="Clear">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
        </button>
      </div>
    </div>

    <div class="sidebar-scroll">
      <div class="nav-section">
        <button class="nav-item" data-view="all" id="navAll">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></svg>
          <span class="label">All Notes</span>
          <span class="count" id="countAll">0</span>
        </button>
        <button class="nav-item" data-view="pinned" id="navPinned">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>
          <span class="label">Pinned</span>
          <span class="count" id="countPinned">0</span>
        </button>
        <button class="nav-item" data-view="recent" id="navRecent">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
          <span class="label">Recent</span>
        </button>
      </div>

      <div class="nav-section">
        <div class="nav-label">
          Notebooks
          <button class="icon-btn sm" id="btnAddNotebook" title="New notebook">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5v14"/></svg>
          </button>
        </div>
        <div id="notebookList"></div>
      </div>

      <div class="nav-section" id="tagsSection" style="display:none">
        <div class="nav-label">Tags</div>
        <div id="tagsList"></div>
      </div>
    </div>

    <div class="sidebar-footer">
      <button class="btn btn-soft btn-block" id="btnNewNote">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5v14"/></svg>
        New Note
      </button>
    </div>
  </aside>

  <!-- Note list -->
  <div class="note-list-panel" id="noteListPanel">
    <div class="list-header">
      <button class="icon-btn" id="btnToggleSidebar" title="Toggle sidebar">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/></svg>
      </button>
      <h2 id="listTitle">All Notes</h2>
      <button class="icon-btn" id="btnNewNoteHeader" title="New note (⌘N)">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5v14"/></svg>
      </button>
    </div>
    <div class="sort-row">
      <button class="chip active" data-sort="updated">Recent</button>
      <button class="chip" data-sort="created">Created</button>
      <button class="chip" data-sort="title">Title</button>
    </div>
    <div class="list-meta" id="listMeta">0 notes</div>
    <div class="notes-scroll" id="notesScroll"></div>
  </div>

  <!-- Editor -->
  <main class="editor-panel" id="editorPanel">
    <div id="editorToolbar" class="editor-toolbar" style="display:none">
      <button class="icon-btn" id="btnBackList" title="Back to list" style="display:none">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
      </button>
      <div class="toolbar-group">
        <button class="tb-btn" data-cmd="bold" title="Bold (⌘B)"><b>B</b></button>
        <button class="tb-btn" data-cmd="italic" title="Italic (⌘I)"><i>I</i></button>
        <button class="tb-btn" data-cmd="underline" title="Underline (⌘U)"><u style="font-size:13px;font-weight:500">U</u></button>
        <button class="tb-btn" data-cmd="strikeThrough" title="Strikethrough"><s style="font-size:13px">S</s></button>
      </div>
      <div class="toolbar-group">
        <button class="tb-btn" data-cmd="formatBlock" data-value="h1" title="Heading 1">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8M4 18V6M12 18V6M17 12l3-2v8"/></svg>
        </button>
        <button class="tb-btn" data-cmd="formatBlock" data-value="h2" title="Heading 2">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M4 12h8M4 18V6M12 18V6M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1"/></svg>
        </button>
        <button class="tb-btn" data-cmd="formatBlock" data-value="p" title="Paragraph">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M13 4v16M17 4v16M19 4H9.5a4.5 4.5 0 0 0 0 9H13"/></svg>
        </button>
      </div>
      <div class="toolbar-group">
        <button class="tb-btn" data-cmd="insertUnorderedList" title="Bullet list">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/></svg>
        </button>
        <button class="tb-btn" data-cmd="insertOrderedList" title="Numbered list">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><line x1="10" x2="21" y1="6" y2="6"/><line x1="10" x2="21" y1="12" y2="12"/><line x1="10" x2="21" y1="18" y2="18"/><path d="M4 6h1v4M4 10h2M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg>
        </button>
        <button class="tb-btn" data-cmd="formatBlock" data-value="blockquote" title="Quote">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V21"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3"/></svg>
        </button>
        <button class="tb-btn" data-cmd="formatBlock" data-value="pre" title="Code block">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
        </button>
      </div>
      <div class="toolbar-group">
        <button class="tb-btn" id="btnInsertLink" title="Insert link">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
        </button>
        <button class="tb-btn" data-cmd="removeFormat" title="Clear formatting">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h16v3M9 20h6M12 4v16"/></svg>
        </button>
      </div>
      <div class="spacer"></div>
      <div class="toolbar-group">
        <button class="tb-btn" id="btnPin" title="Pin note">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>
        </button>
        <button class="tb-btn" id="btnMoveNote" title="Move to notebook">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>
        </button>
        <button class="tb-btn" id="btnFocusMode" title="Focus mode (⌘.)">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
        </button>
        <button class="tb-btn danger" id="btnDeleteNote" title="Delete note" style="color:var(--text-secondary)">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
        </button>
      </div>
    </div>

    <div class="editor-body" id="editorBody">
      <div class="editor-empty" id="editorEmpty">
        <div class="empty-icon">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>
        </div>
        <h3>Select a note or create one</h3>
        <p>Your thoughts, ideas, and knowledge — beautifully organized in one place.</p>
        <button class="btn btn-primary" id="btnEmptyNew" style="margin-top:8px">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="15" height="15"><path d="M5 12h14M12 5v14"/></svg>
          Create note
        </button>
        <div class="shortcuts">
          <div class="shortcut"><kbd>⌘</kbd><kbd>N</kbd> New note</div>
          <div class="shortcut"><kbd>⌘</kbd><kbd>K</kbd> Search</div>
          <div class="shortcut"><kbd>⌘</kbd><kbd>.</kbd> Focus</div>
        </div>
      </div>

      <div class="editor-inner" id="editorInner" style="display:none">
        <div class="editor-meta-bar">
          <span class="meta-date" id="editorDate"></span>
          <div class="editor-tags" id="editorTags"></div>
        </div>
        <textarea class="note-title" id="noteTitle" placeholder="Untitled" rows="1"></textarea>
        <div class="note-content" id="noteContent" contenteditable="true" data-placeholder="Start writing… Type / for commands or just begin." spellcheck="true"></div>
      </div>
    </div>

    <div class="status-bar" id="statusBar" style="display:none">
      <span class="dot"></span>
      <span id="statusSaved">Saved</span>
      <div class="stat-right">
        <span id="statusWords">0 words</span>
        <span id="statusChars">0 characters</span>
      </div>
    </div>
  </main>
</div>

<!-- Notebook modal -->
<div class="modal-overlay" id="notebookModal">
  <div class="modal">
    <h3 id="nbModalTitle">New Notebook</h3>
    <p class="modal-desc">Organize notes into notebooks by project, topic, or purpose.</p>
    <div class="modal-field">
      <label for="nbName">Name</label>
      <input type="text" id="nbName" placeholder="e.g. Work, Personal, Research" maxlength="40" />
    </div>
    <div class="modal-field">
      <label>Color</label>
      <div class="color-picks" id="colorPicks"></div>
    </div>
    <div class="modal-actions">
      <button class="btn btn-ghost" id="nbCancel">Cancel</button>
      <button class="btn btn-primary" id="nbSave">Create</button>
    </div>
  </div>
</div>

<!-- Move note modal -->
<div class="modal-overlay" id="moveModal">
  <div class="modal">
    <h3>Move to Notebook</h3>
    <p class="modal-desc">Choose a notebook for this note.</p>
    <div class="modal-field">
      <label for="moveSelect">Notebook</label>
      <select id="moveSelect"></select>
    </div>
    <div class="modal-actions">
      <button class="btn btn-ghost" id="moveCancel">Cancel</button>
      <button class="btn btn-primary" id="moveSave">Move</button>
    </div>
  </div>
</div>

<!-- Confirm delete -->
<div class="modal-overlay" id="deleteModal">
  <div class="modal">
    <h3>Delete note?</h3>
    <p class="modal-desc">This action cannot be undone. The note will be permanently removed.</p>
    <div class="modal-actions">
      <button class="btn btn-ghost" id="delCancel">Cancel</button>
      <button class="btn btn-danger" id="delConfirm">Delete</button>
    </div>
  </div>
</div>

<!-- Tag input modal -->
<div class="modal-overlay" id="tagModal">
  <div class="modal">
    <h3>Add Tag</h3>
    <p class="modal-desc">Tags help you find related notes across notebooks.</p>
    <div class="modal-field">
      <label for="tagInput">Tag name</label>
      <input type="text" id="tagInput" placeholder="e.g. ideas, todo, reference" maxlength="24" />
    </div>
    <div class="modal-actions">
      <button class="btn btn-ghost" id="tagCancel">Cancel</button>
      <button class="btn btn-primary" id="tagSave">Add</button>
    </div>
  </div>
</div>

<!-- Command palette -->
<div class="cmd-overlay" id="cmdOverlay">
  <div class="cmd-palette">
    <div class="cmd-input-wrap">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
      <input type="text" id="cmdInput" placeholder="Search notes and commands…" autocomplete="off" />
      <kbd style="flex-shrink:0">ESC</kbd>
    </div>
    <div class="cmd-results" id="cmdResults"></div>
  </div>
</div>

<!-- Context menu -->
<div class="context-menu" id="contextMenu"></div>

<div class="toasts" id="toasts"></div>

<script>
(function () {
  'use strict';

  // ─── Icons (inline SVG snippets) ───
  const I = {
    pin: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>',
    trash: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>',
    edit: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>',
    folder: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>',
    check: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
    info: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>',
    note: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="M14 2v6h6"/></svg>',
    moon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>',
    sun: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>',
    x: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>',
    tag: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2H2v10l9.29 9.29c.94.94 2.48.94 3.42 0l6.58-6.58c.94-.94.94-2.48 0-3.42L12 2Z"/><path d="M7 7h.01"/></svg>',
    plus: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5v14"/></svg>',
  };

  const COLORS = ['#5b6cfa','#a78bfa','#f472b6','#fb7185','#fbbf24','#34d399','#22d3ee','#60a5fa','#fb923c','#94a3b8'];
  const STORAGE_KEY = 'lumen-notes-v1';

  // ─── State ───
  let state = {
    theme: 'light',
    notebooks: [],
    notes: [],
    view: { type: 'all' }, // all | pinned | recent | notebook | tag | search
    selectedNoteId: null,
    sort: 'updated',
    searchQuery: '',
    focusMode: false,
  };

  let saveTimer = null;
  let editingNotebookId = null;
  let selectedColor = COLORS[0];
  let ctxTarget = null;

  // ─── Sample data ───
  function seedData() {
    const now = Date.now();
    const nb = [
      { id: uid(), name: 'Personal', color: COLORS[0] },
      { id: uid(), name: 'Work', color: COLORS[5] },
      { id: uid(), name: 'Ideas', color: COLORS[1] },
    ];
    const notes = [
      {
        id: uid(),
        title: 'Welcome to Lumen Notes',
        content: `<p>Lumen is a lightweight notes app inspired by Obsidian, UpNote, and Notion. Everything lives in your browser — no account required.</p>
<h2>What you can do</h2>
<ul>
<li><b>Create notebooks</b> to organize by project or topic</li>
<li><b>Pin important notes</b> so they stay at the top</li>
<li><b>Tag notes</b> for cross-cutting labels</li>
<li><b>Rich text editing</b> — bold, headings, lists, quotes, code</li>
<li><b>Search everything</b> with ⌘K</li>
<li><b>Focus mode</b> with ⌘. for distraction-free writing</li>
</ul>
<blockquote>All notes are saved automatically to local storage.</blockquote>
<h2>Keyboard shortcuts</h2>
<ul>
<li><code>⌘/Ctrl + N</code> — New note</li>
<li><code>⌘/Ctrl + K</code> — Command palette / search</li>
<li><code>⌘/Ctrl + .</code> — Toggle focus mode</li>
<li><code>⌘/Ctrl + B / I / U</code> — Bold, italic, underline</li>
</ul>
<p>Select a different notebook, create a new note, and make this space yours.</p>`,
        notebookId: nb[0].id,
        tags: ['getting-started', 'tips'],
        pinned: true,
        createdAt: now - 86400000 * 3,
        updatedAt: now - 3600000,
      },
      {
        id: uid(),
        title: 'Weekly planning',
        content: `<h2>This week</h2>
<ul>
<li>Review Q1 goals</li>
<li>Ship the onboarding flow</li>
<li>Write design doc for search</li>
</ul>
<h3>Later</h3>
<ul>
<li>Team retro notes</li>
<li>UpdateHire plan</li>
</ul>
<p>Focus theme: <b>depth over breadth</b>.</p>`,
        notebookId: nb[1].id,
        tags: ['planning', 'work'],
        pinned: false,
        createdAt: now - 86400000 * 2,
        updatedAt: now - 7200000,
      },
      {
        id: uid(),
        title: 'Product ideas dump',
        content: `<p>Random sparks — filter later.</p>
<ol>
<li>Smart notebook suggestions based on content</li>
<li>Bi-directional links between notes</li>
<li>Daily note auto-create at midnight</li>
<li>Export as Markdown / PDF</li>
<li>Canvas view for spatial thinking</li>
</ol>
<blockquote>Capture first. Organize second. Perfect never.</blockquote>`,
        notebookId: nb[2].id,
        tags: ['ideas', 'product'],
        pinned: true,
        createdAt: now - 86400000,
        updatedAt: now - 1800000,
      },
      {
        id: uid(),
        title: 'Meeting notes — design sync',
        content: `<p><b>Attendees:</b> Alex, Sam, Jordan</p>
<p><b>Date:</b> Today</p>
<hr>
<h3>Decisions</h3>
<ul>
<li>Ship MVP with three core flows only</li>
<li>Postpone dark theme polish until next sprint</li>
</ul>
<h3>Action items</h3>
<ul>
<li>Alex — Figma passes by Friday</li>
<li>Sam — API contracts drafted</li>
<li>Jordan — User testing script</li>
</ul>`,
        notebookId: nb[1].id,
        tags: ['meetings'],
        pinned: false,
        createdAt: now - 86400000 * 0.5,
        updatedAt: now - 900000,
      },
      {
        id: uid(),
        title: 'Books to read',
        content: `<ul>
<li>Thinking, Fast and Slow — Kahneman</li>
<li>The Design of Everyday Things — Norman</li>
<li>Staff Engineer — Larson</li>
<li>Atomic Habits — Clear</li>
</ul>
<p>Currently reading: <i>Range</i> by David Epstein.</p>`,
        notebookId: nb[0].id,
        tags: ['reading'],
        pinned: false,
        createdAt: now - 86400000 * 5,
        updatedAt: now - 86400000 * 4,
      },
    ];
    state.notebooks = nb;
    state.notes = notes;
    state.selectedNoteId = notes[0].id;
  }

  // ─── Utils ───
  function uid() {
    return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
  }

  function stripHtml(html) {
    const d = document.createElement('div');
    d.innerHTML = html || '';
    return (d.textContent || d.innerText || '').replace(/\s+/g, ' ').trim();
  }

  function formatDate(ts) {
    const d = new Date(ts);
    const now = new Date();
    const diff = now - d;
    if (diff < 60000) return 'Just now';
    if (diff < 3600000) return Math.floor(diff / 60000) + 'm ago';
    if (diff < 86400000) return Math.floor(diff / 3600000) + 'h ago';
    if (diff < 86400000 * 7) return Math.floor(diff / 86400000) + 'd ago';
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: d.getFullYear() !== now.getFullYear() ? 'numeric' : undefined });
  }

  function formatFullDate(ts) {
    return new Date(ts).toLocaleString(undefined, {
      weekday: 'short', month: 'short', day: 'numeric', year: 'numeric',
      hour: 'numeric', minute: '2-digit'
    });
  }

  function wordCount(html) {
    const t = stripHtml(html);
    if (!t) return 0;
    return t.split(/\s+/).filter(Boolean).length;
  }

  function debounce(fn, ms) {
    let t;
    return function (...args) {
      clearTimeout(t);
      t = setTimeout(() => fn.apply(this, args), ms);
    };
  }

  function isMod(e) {
    return e.metaKey || e.ctrlKey;
  }

  // ─── Persistence ───
  function load() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) {
        const data = JSON.parse(raw);
        state.theme = data.theme || 'light';
        state.notebooks = data.notebooks || [];
        state.notes = data.notes || [];
        state.selectedNoteId = data.selectedNoteId || null;
        state.sort = data.sort || 'updated';
        if (!state.notebooks.length && !state.notes.length) seedData();
      } else {
        seedData();
      }
    } catch {
      seedData();
    }
  }

  function persist() {
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify({
        theme: state.theme,
        notebooks: state.notebooks,
        notes: state.notes,
        selectedNoteId: state.selectedNoteId,
        sort: state.sort,
      }));
    } catch (e) {
      console.warn('Storage full or unavailable', e);
    }
  }

  // ─── Filtering / Sorting ───
  function getFilteredNotes() {
    let notes = [...state.notes];
    const v = state.view;

    if (v.type === 'pinned') notes = notes.filter(n => n.pinned);
    else if (v.type === 'recent') {
      notes = notes.sort((a, b) => b.updatedAt - a.updatedAt).slice(0, 20);
    } else if (v.type === 'notebook') notes = notes.filter(n => n.notebookId === v.id);
    else if (v.type === 'tag') notes = notes.filter(n => (n.tags || []).includes(v.tag));
    else if (v.type === 'search') {
      const q = (state.searchQuery || '').toLowerCase();
      notes = notes.filter(n =>
        (n.title || '').toLowerCase().includes(q) ||
        stripHtml(n.content).toLowerCase().includes(q) ||
        (n.tags || []).some(t => t.toLowerCase().includes(q))
      );
    }

    if (v.type !== 'recent') {
      notes.sort((a, b) => {
        if (a.pinned !== b.pinned) return a.pinned ? -1 : 1;
        if (state.sort === 'title') return (a.title || '').localeCompare(b.title || '');
        if (state.sort === 'created') return b.createdAt - a.createdAt;
        return b.updatedAt - a.updatedAt;
      });
    }

    return notes;
  }

  function getSelectedNote() {
    return state.notes.find(n => n.id === state.selectedNoteId) || null;
  }

  function allTags() {
    const map = {};
    state.notes.forEach(n => (n.tags || []).forEach(t => { map[t] = (map[t] || 0) + 1; }));
    return Object.entries(map).sort((a, b) => b[1] - a[1]);
  }

  // ─── Rendering ───
  function applyTheme() {
    document.documentElement.setAttribute('data-theme', state.theme);
    const btn = document.getElementById('btnTheme');
    btn.innerHTML = state.theme === 'dark' ? I.sun : I.moon;
    btn.title = state.theme === 'dark' ? 'Light mode' : 'Dark mode';
  }

  function renderNotebooks() {
    const el = document.getElementById('notebookList');
    el.innerHTML = state.notebooks.map(nb => {
      const count = state.notes.filter(n => n.notebookId === nb.id).length;
      const active = state.view.type === 'notebook' && state.view.id === nb.id;
      return `
        <button class="nav-item ${active ? 'active' : ''}" data-notebook="${nb.id}">
          <span class="notebook-dot" style="background:${nb.color}"></span>
          <span class="label">${esc(nb.name)}</span>
          <span class="count">${count}</span>
          <span class="item-actions">
            <span class="icon-btn sm" data-edit-nb="${nb.id}" title="Rename"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></span>
            <span class="icon-btn sm danger" data-del-nb="${nb.id}" title="Delete"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/></svg></span>
          </span>
        </button>`;
    }).join('');
  }

  function renderTags() {
    const tags = allTags();
    const section = document.getElementById('tagsSection');
    const list = document.getElementById('tagsList');
    if (!tags.length) { section.style.display = 'none'; return; }
    section.style.display = '';
    list.innerHTML = tags.map(([tag, count]) => {
      const active = state.view.type === 'tag' && state.view.tag === tag;
      return `
        <button class="nav-item ${active ? 'active' : ''}" data-tag="${esc(tag)}">
          ${I.tag}
          <span class="label">${esc(tag)}</span>
          <span class="count">${count}</span>
        </button>`;
    }).join('');
  }

  function renderNavCounts() {
    document.getElementById('countAll').textContent = state.notes.length;
    document.getElementById('countPinned').textContent = state.notes.filter(n => n.pinned).length;

    document.querySelectorAll('.nav-item[data-view]').forEach(el => {
      el.classList.toggle('active', state.view.type === el.dataset.view);
    });
  }

  function renderNoteList() {
    const notes = getFilteredNotes();
    const scroll = document.getElementById('notesScroll');
    const meta = document.getElementById('listMeta');
    const title = document.getElementById('listTitle');

    let titleText = 'All Notes';
    if (state.view.type === 'pinned') titleText = 'Pinned';
    else if (state.view.type === 'recent') titleText = 'Recent';
    else if (state.view.type === 'search') titleText = `Search: “${state.searchQuery}”`;
    else if (state.view.type === 'notebook') {
      const nb = state.notebooks.find(n => n.id === state.view.id);
      titleText = nb ? nb.name : 'Notebook';
    } else if (state.view.type === 'tag') titleText = `#${state.view.tag}`;
    title.textContent = titleText;
    meta.textContent = notes.length + (notes.length === 1 ? ' note' : ' notes');

    document.querySelectorAll('.chip[data-sort]').forEach(c => {
      c.classList.toggle('active', c.dataset.sort === state.sort);
    });

    if (!notes.length) {
      scroll.innerHTML = `
        <div class="empty-list">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="M14 2v6h6"/></svg>
          <p>${state.searchQuery ? 'No notes match your search.' : 'No notes here yet. Create one!'}</p>
        </div>`;
      return;
    }

    scroll.innerHTML = notes.map(n => {
      const preview = stripHtml(n.content).slice(0, 120);
      const active = n.id === state.selectedNoteId;
      const tags = (n.tags || []).slice(0, 3).map(t => `<span class="tag">${esc(t)}</span>`).join('');
      return `
        <div class="note-card ${active ? 'active' : ''}" data-note-id="${n.id}" role="button" tabindex="0">
          <div class="note-card-top">
            <div class="note-card-title">${esc(n.title || 'Untitled')}</div>
            ${n.pinned ? `<span class="pin-icon">${I.pin}</span>` : ''}
          </div>
          ${preview ? `<div class="note-card-preview">${esc(preview)}</div>` : ''}
          <div class="note-card-footer">
            <span class="note-card-date">${formatDate(n.updatedAt)}</span>
            ${tags}
          </div>
        </div>`;
    }).join('');
  }

  function renderEditor() {
    const note = getSelectedNote();
    const empty = document.getElementById('editorEmpty');
    const inner = document.getElementById('editorInner');
    const toolbar = document.getElementById('editorToolbar');
    const status = document.getElementById('statusBar');

    if (!note) {
      empty.style.display = '';
      inner.style.display = 'none';
      toolbar.style.display = 'none';
      status.style.display = 'none';
      return;
    }

    empty.style.display = 'none';
    inner.style.display = '';
    toolbar.style.display = 'flex';
    status.style.display = 'flex';

    const titleEl = document.getElementById('noteTitle');
    const contentEl = document.getElementById('noteContent');

    if (titleEl.dataset.noteId !== note.id) {
      titleEl.value = note.title || '';
      titleEl.dataset.noteId = note.id;
      autoResizeTitle();
      contentEl.innerHTML = note.content || '';
      contentEl.dataset.noteId = note.id;
    }

    document.getElementById('editorDate').textContent =
      `Updated ${formatFullDate(note.updatedAt)} · Created ${formatDate(note.createdAt)}`;

    renderEditorTags(note);
    updateWordCount(note.content);
    document.getElementById('btnPin').classList.toggle('active', !!note.pinned);
    document.getElementById('statusSaved').textContent = 'Saved';
  }

  function renderEditorTags(note) {
    const el = document.getElementById('editorTags');
    const tags = note.tags || [];
    el.innerHTML = tags.map(t => `
      <span class="editor-tag">
        ${esc(t)}
        <button data-remove-tag="${esc(t)}" title="Remove tag">${I.x}</button>
      </span>
    `).join('') + `<button class="add-tag-btn" id="btnAddTag">+ Tag</button>`;
  }

  function updateWordCount(html) {
    const text = stripHtml(html);
    const words = wordCount(html);
    document.getElementById('statusWords').textContent = words + (words === 1 ? ' word' : ' words');
    document.getElementById('statusChars').textContent = text.length + ' characters';
  }

  function autoResizeTitle() {
    const el = document.getElementById('noteTitle');
    el.style.height = 'auto';
    el.style.height = el.scrollHeight + 'px';
  }

  function fullRender() {
    applyTheme();
    renderNotebooks();
    renderTags();
    renderNavCounts();
    renderNoteList();
    renderEditor();
  }

  function esc(s) {
    const d = document.createElement('div');
    d.textContent = String(s == null ? '' : s);
    return d.innerHTML;
  }

  // ─── Actions ───
  function createNote(opts = {}) {
    const notebookId = opts.notebookId ||
      (state.view.type === 'notebook' ? state.view.id : null) ||
      (state.notebooks[0] && state.notebooks[0].id) || null;

    const note = {
      id: uid(),
      title: '',
      content: '',
      notebookId,
      tags: opts.tags || [],
      pinned: false,
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    state.notes.unshift(note);
    state.selectedNoteId = note.id;
    persist();
    fullRender();
    // focus title
    requestAnimationFrame(() => {
      const t = document.getElementById('noteTitle');
      t.focus();
      document.getElementById('app').classList.add('mobile-editor');
    });
    toast('Note created', 'success');
    return note;
  }

  function deleteNote(id) {
    const idx = state.notes.findIndex(n => n.id === id);
    if (idx === -1) return;
    state.notes.splice(idx, 1);
    if (state.selectedNoteId === id) {
      state.selectedNoteId = state.notes[0] ? state.notes[0].id : null;
    }
    persist();
    fullRender();
    toast('Note deleted', 'info');
  }

  function updateNote(id, patch) {
    const note = state.notes.find(n => n.id === id);
    if (!note) return;
    Object.assign(note, patch, { updatedAt: Date.now() });
    document.getElementById('statusSaved').textContent = 'Saving…';
    clearTimeout(saveTimer);
    saveTimer = setTimeout(() => {
      persist();
      document.getElementById('statusSaved').textContent = 'Saved';
      // light refresh list without resetting editor focus
      renderNoteList();
      renderNavCounts();
      renderNotebooks();
      renderTags();
      const n = getSelectedNote();
      if (n) {
        document.getElementById('editorDate').textContent =
          `Updated ${formatFullDate(n.updatedAt)} · Created ${formatDate(n.createdAt)}`;
      }
    }, 400);
  }

  function togglePin(id) {
    const note = state.notes.find(n => n.id === id);
    if (!note) return;
    note.pinned = !note.pinned;
    note.updatedAt = Date.now();
    persist();
    fullRender();
    toast(note.pinned ? 'Pinned' : 'Unpinned', 'success');
  }

  function createNotebook(name, color) {
    const nb = { id: uid(), name: name.trim(), color };
    state.notebooks.push(nb);
    persist();
    fullRender();
    toast('Notebook created', 'success');
    return nb;
  }

  function updateNotebook(id, name, color) {
    const nb = state.notebooks.find(n => n.id === id);
    if (!nb) return;
    nb.name = name.trim();
    nb.color = color;
    persist();
    fullRender();
    toast('Notebook updated', 'success');
  }

  function deleteNotebook(id) {
    state.notebooks = state.notebooks.filter(n => n.id !== id);
    state.notes.forEach(n => {
      if (n.notebookId === id) n.notebookId = state.notebooks[0] ? state.notebooks[0].id : null;
    });
    if (state.view.type === 'notebook' && state.view.id === id) {
      state.view = { type: 'all' };
    }
    persist();
    fullRender();
    toast('Notebook deleted', 'info');
  }

  // ─── Toasts ───
  function toast(msg, type = 'info') {
    const el = document.createElement('div');
    el.className = `toast ${type}`;
    el.innerHTML = (type === 'success' ? I.check : I.info) + `<span>${esc(msg)}</span>`;
    document.getElementById('toasts').appendChild(el);
    setTimeout(() => {
      el.classList.add('out');
      setTimeout(() => el.remove(), 200);
    }, 2200);
  }

  // ─── Modals ───
  function openModal(id) {
    document.getElementById(id).classList.add('open');
  }
  function closeModal(id) {
    document.getElementById(id).classList.remove('open');
  }

  function openNotebookModal(editId) {
    editingNotebookId = editId || null;
    selectedColor = COLORS[0];
    const title = document.getElementById('nbModalTitle');
    const save = document.getElementById('nbSave');
    const input = document.getElementById('nbName');

    if (editId) {
      const nb = state.notebooks.find(n => n.id === editId);
      title.textContent = 'Edit Notebook';
      save.textContent = 'Save';
      input.value = nb ? nb.name : '';
      selectedColor = nb ? nb.color : COLORS[0];
    } else {
      title.textContent = 'New Notebook';
      save.textContent = 'Create';
      input.value = '';
    }

    const picks = document.getElementById('colorPicks');
    picks.innerHTML = COLORS.map(c =>
      `<button type="button" class="color-pick ${c === selectedColor ? 'selected' : ''}" data-color="${c}" style="background:${c}"></button>`
    ).join('');

    openModal('notebookModal');
    setTimeout(() => input.focus(), 50);
  }

  // ─── Command palette ───
  function openCmd() {
    const overlay = document.getElementById('cmdOverlay');
    overlay.classList.add('open');
    const input = document.getElementById('cmdInput');
    input.value = '';
    renderCmdResults('');
    setTimeout(() => input.focus(), 30);
  }

  function closeCmd() {
    document.getElementById('cmdOverlay').classList.remove('open');
  }

  function renderCmdResults(q) {
    const results = document.getElementById('cmdResults');
    const query = (q || '').toLowerCase().trim();
    const items = [];

    // Commands
    const commands = [
      { id: 'new', label: 'New note', hint: '⌘N', icon: I.plus, action: () => { closeCmd(); createNote(); } },
      { id: 'new-nb', label: 'New notebook', hint: '', icon: I.folder, action: () => { closeCmd(); openNotebookModal(); } },
      { id: 'theme', label: 'Toggle theme', hint: '', icon: state.theme === 'dark' ? I.sun : I.moon, action: () => { closeCmd(); toggleTheme(); } },
      { id: 'focus', label: 'Toggle focus mode', hint: '⌘.', icon: I.note, action: () => { closeCmd(); toggleFocus(); } },
      { id: 'all', label: 'Go to All Notes', hint: '', icon: I.note, action: () => { closeCmd(); setView({ type: 'all' }); } },
      { id: 'pinned', label: 'Go to Pinned', hint: '', icon: I.pin, action: () => { closeCmd(); setView({ type: 'pinned' }); } },
    ];

    if (!query || 'new note create'.includes(query)) items.push(commands[0]);
    if (!query || 'notebook folder'.includes(query) || commands[1].label.toLowerCase().includes(query)) items.push(commands[1]);
    if (!query || commands[2].label.toLowerCase().includes(query) || 'dark light'.includes(query)) items.push(commands[2]);
    if (!query || commands[3].label.toLowerCase().includes(query) || 'focus'.includes(query)) items.push(commands[3]);
    if (!query || 'all notes'.includes(query)) items.push(commands[4]);
    if (!query || 'pinned'.includes(query)) items.push(commands[5]);

    // Notes match
    if (query) {
      state.notes.forEach(n => {
        const hay = ((n.title || '') + ' ' + stripHtml(n.content) + ' ' + (n.tags || []).join(' ')).toLowerCase();
        if (hay.includes(query)) {
          items.push({
            id: 'note-' + n.id,
            label: n.title || 'Untitled',
            hint: formatDate(n.updatedAt),
            icon: I.note,
            action: () => {
              closeCmd();
              state.selectedNoteId = n.id;
              fullRender();
              document.getElementById('app').classList.add('mobile-editor');
            },
          });
        }
      });
    }

    if (!items.length) {
      results.innerHTML = `<div class="cmd-empty">No results</div>`;
      return;
    }

    results.innerHTML = items.map((item, i) => `
      <div class="cmd-item ${i === 0 ? 'highlighted' : ''}" data-cmd-idx="${i}">
        <div class="cmd-icon">${item.icon}</div>
        <span class="cmd-label">${esc(item.label)}</span>
        ${item.hint ? `<span class="cmd-hint">${esc(item.hint)}</span>` : ''}
      </div>
    `).join('');

    results._items = items;
    results._hi = 0;
  }

  function runHighlightedCmd() {
    const results = document.getElementById('cmdResults');
    const items = results._items || [];
    const hi = results._hi || 0;
    if (items[hi]) items[hi].action();
  }

  // ─── View helpers ───
  function setView(view) {
    state.view = view;
    if (view.type !== 'search') {
      state.searchQuery = '';
      document.getElementById('searchInput').value = '';
      document.getElementById('clearSearch').classList.remove('visible');
    }
    fullRender();
  }

  function toggleTheme() {
    state.theme = state.theme === 'dark' ? 'light' : 'dark';
    persist();
    applyTheme();
  }

  function toggleFocus() {
    state.focusMode = !state.focusMode;
    document.getElementById('app').classList.toggle('focus-mode', state.focusMode);
    document.getElementById('btnFocusMode').classList.toggle('active', state.focusMode);
    if (state.focusMode) toast('Focus mode on — press ⌘. to exit', 'info');
  }

  // ─── Context menu ───
  function showContextMenu(x, y, items) {
    const menu = document.getElementById('contextMenu');
    menu.innerHTML = items.map(it => {
      if (it.sep) return '<div class="ctx-sep"></div>';
      return `<button class="ctx-item ${it.danger ? 'danger' : ''}" data-ctx="${it.id}">${it.icon || ''}<span>${esc(it.label)}</span></button>`;
    }).join('');
    menu._actions = {};
    items.forEach(it => { if (it.id) menu._actions[it.id] = it.action; });

    menu.style.display = 'block';
    menu.classList.add('open');
    const rect = menu.getBoundingClientRect();
    let left = x, top = y;
    if (left + rect.width > window.innerWidth - 8) left = window.innerWidth - rect.width - 8;
    if (top + rect.height > window.innerHeight - 8) top = window.innerHeight - rect.height - 8;
    menu.style.left = left + 'px';
    menu.style.top = top + 'px';
  }

  function hideContextMenu() {
    const menu = document.getElementById('contextMenu');
    menu.classList.remove('open');
    menu.style.display = 'none';
  }

  // ─── Event wiring ───
  function bindEvents() {
    // Theme
    document.getElementById('btnTheme').addEventListener('click', toggleTheme);

    // New note
    document.getElementById('btnNewNote').addEventListener('click', () => createNote());
    document.getElementById('btnNewNoteHeader').addEventListener('click', () => createNote());
    document.getElementById('btnEmptyNew').addEventListener('click', () => createNote());

    // Sidebar toggle
    document.getElementById('btnToggleSidebar').addEventListener('click', () => {
      document.getElementById('app').classList.toggle('sidebar-collapsed');
    });

    // Nav views
    document.getElementById('sidebar').addEventListener('click', (e) => {
      const viewBtn = e.target.closest('[data-view]');
      if (viewBtn) {
        setView({ type: viewBtn.dataset.view });
        return;
      }
      const nbBtn = e.target.closest('[data-notebook]');
      if (nbBtn && !e.target.closest('[data-edit-nb]') && !e.target.closest('[data-del-nb]')) {
        setView({ type: 'notebook', id: nbBtn.dataset.notebook });
        return;
      }
      const editNb = e.target.closest('[data-edit-nb]');
      if (editNb) {
        e.stopPropagation();
        openNotebookModal(editNb.dataset.editNb);
        return;
      }
      const delNb = e.target.closest('[data-del-nb]');
      if (delNb) {
        e.stopPropagation();
        const id = delNb.dataset.delNb;
        const nb = state.notebooks.find(n => n.id === id);
        if (nb && confirm(`Delete notebook “${nb.name}”? Notes will be moved to another notebook.`)) {
          deleteNotebook(id);
        }
        return;
      }
      const tagBtn = e.target.closest('[data-tag]');
      if (tagBtn) {
        setView({ type: 'tag', tag: tagBtn.dataset.tag });
      }
    });

    document.getElementById('btnAddNotebook').addEventListener('click', () => openNotebookModal());

    // Search
    const searchInput = document.getElementById('searchInput');
    const clearSearch = document.getElementById('clearSearch');
    searchInput.addEventListener('input', () => {
      const q = searchInput.value;
      clearSearch.classList.toggle('visible', !!q);
      state.searchQuery = q;
      if (q.trim()) {
        state.view = { type: 'search' };
      } else {
        state.view = { type: 'all' };
      }
      renderNoteList();
      renderNavCounts();
    });
    clearSearch.addEventListener('click', () => {
      searchInput.value = '';
      clearSearch.classList.remove('visible');
      state.searchQuery = '';
      setView({ type: 'all' });
    });

    // Sort chips
    document.querySelector('.sort-row').addEventListener('click', (e) => {
      const chip = e.target.closest('[data-sort]');
      if (!chip) return;
      state.sort = chip.dataset.sort;
      persist();
      renderNoteList();
    });

    // Note list selection
    document.getElementById('notesScroll').addEventListener('click', (e) => {
      const card = e.target.closest('[data-note-id]');
      if (!card) return;
      state.selectedNoteId = card.dataset.noteId;
      // force editor swap
      document.getElementById('noteTitle').dataset.noteId = '';
      renderEditor();
      renderNoteList();
      document.getElementById('app').classList.add('mobile-editor');
    });

    document.getElementById('notesScroll').addEventListener('contextmenu', (e) => {
      const card = e.target.closest('[data-note-id]');
      if (!card) return;
      e.preventDefault();
      const id = card.dataset.noteId;
      const note = state.notes.find(n => n.id === id);
      if (!note) return;
      showContextMenu(e.clientX, e.clientY, [
        { id: 'open', label: 'Open', icon: I.note, action: () => {
          state.selectedNoteId = id;
          document.getElementById('noteTitle').dataset.noteId = '';
          fullRender();
        }},
        { id: 'pin', label: note.pinned ? 'Unpin' : 'Pin', icon: I.pin, action: () => togglePin(id) },
        { sep: true },
        { id: 'del', label: 'Delete', icon: I.trash, danger: true, action: () => {
          state.selectedNoteId = id;
          openModal('deleteModal');
        }},
      ]);
    });

    // Title / content editing
    const titleEl = document.getElementById('noteTitle');
    titleEl.addEventListener('input', () => {
      autoResizeTitle();
      const id = titleEl.dataset.noteId;
      if (id) updateNote(id, { title: titleEl.value });
    });

    const contentEl = document.getElementById('noteContent');
    contentEl.addEventListener('input', () => {
      const id = contentEl.dataset.noteId;
      if (id) {
        updateNote(id, { content: contentEl.innerHTML });
        updateWordCount(contentEl.innerHTML);
      }
    });

    // Toolbar formatting
    document.getElementById('editorToolbar').addEventListener('click', (e) => {
      const btn = e.target.closest('[data-cmd]');
      if (!btn) return;
      e.preventDefault();
      const cmd = btn.dataset.cmd;
      const val = btn.dataset.value || null;
      document.getElementById('noteContent').focus();
      if (cmd === 'formatBlock') {
        document.execCommand('formatBlock', false, val === 'pre' ? 'pre' : val);
      } else {
        document.execCommand(cmd, false, val);
      }
      const id = contentEl.dataset.noteId;
      if (id) {
        updateNote(id, { content: contentEl.innerHTML });
        updateWordCount(contentEl.innerHTML);
      }
    });

    document.getElementById('btnInsertLink').addEventListener('click', () => {
      const url = prompt('Link URL:', 'https://');
      if (!url) return;
      document.getElementById('noteContent').focus();
      document.execCommand('createLink', false, url);
      const id = contentEl.dataset.noteId;
      if (id) updateNote(id, { content: contentEl.innerHTML });
    });

    document.getElementById('btnPin').addEventListener('click', () => {
      if (state.selectedNoteId) togglePin(state.selectedNoteId);
    });

    document.getElementById('btnFocusMode').addEventListener('click', toggleFocus);

    document.getElementById('btnDeleteNote').addEventListener('click', () => {
      if (state.selectedNoteId) openModal('deleteModal');
    });

    document.getElementById('btnMoveNote').addEventListener('click', () => {
      const sel = document.getElementById('moveSelect');
      sel.innerHTML = state.notebooks.map(nb =>
        `<option value="${nb.id}">${esc(nb.name)}</option>`
      ).join('') + `<option value="">(No notebook)</option>`;
      const note = getSelectedNote();
      if (note) sel.value = note.notebookId || '';
      openModal('moveModal');
    });

    document.getElementById('btnBackList').addEventListener('click', () => {
      document.getElementById('app').classList.remove('mobile-editor');
    });

    // Tags in editor
    document.getElementById('editorTags').addEventListener('click', (e) => {
      if (e.target.closest('#btnAddTag') || e.target.id === 'btnAddTag') {
        document.getElementById('tagInput').value = '';
        openModal('tagModal');
        setTimeout(() => document.getElementById('tagInput').focus(), 50);
        return;
      }
      const rm = e.target.closest('[data-remove-tag]');
      if (rm) {
        const tag = rm.dataset.removeTag;
        const note = getSelectedNote();
        if (!note) return;
        note.tags = (note.tags || []).filter(t => t !== tag);
        note.updatedAt = Date.now();
        persist();
        renderEditorTags(note);
        renderNoteList();
        renderTags();
      }
    });

    // Modal: notebook
    document.getElementById('colorPicks').addEventListener('click', (e) => {
      const p = e.target.closest('[data-color]');
      if (!p) return;
      selectedColor = p.dataset.color;
      document.querySelectorAll('.color-pick').forEach(el => {
        el.classList.toggle('selected', el.dataset.color === selectedColor);
      });
    });

    document.getElementById('nbCancel').addEventListener('click', () => closeModal('notebookModal'));
    document.getElementById('nbSave').addEventListener('click', () => {
      const name = document.getElementById('nbName').value.trim();
      if (!name) { document.getElementById('nbName').focus(); return; }
      if (editingNotebookId) updateNotebook(editingNotebookId, name, selectedColor);
      else createNotebook(name, selectedColor);
      closeModal('notebookModal');
    });
    document.getElementById('nbName').addEventListener('keydown', (e) => {
      if (e.key === 'Enter') document.getElementById('nbSave').click();
    });

    // Modal: move
    document.getElementById('moveCancel').addEventListener('click', () => closeModal('moveModal'));
    document.getElementById('moveSave').addEventListener('click', () => {
      const note = getSelectedNote();
      if (note) {
        note.notebookId = document.getElementById('moveSelect').value || null;
        note.updatedAt = Date.now();
        persist();
        fullRender();
        toast('Note moved', 'success');
      }
      closeModal('moveModal');
    });

    // Modal: delete
    document.getElementById('delCancel').addEventListener('click', () => closeModal('deleteModal'));
    document.getElementById('delConfirm').addEventListener('click', () => {
      if (state.selectedNoteId) deleteNote(state.selectedNoteId);
      closeModal('deleteModal');
    });

    // Modal: tag
    document.getElementById('tagCancel').addEventListener('click', () => closeModal('tagModal'));
    document.getElementById('tagSave').addEventListener('click', () => {
      const t = document.getElementById('tagInput').value.trim().toLowerCase().replace(/\s+/g, '-');
      if (!t) return;
      const note = getSelectedNote();
      if (note) {
        note.tags = note.tags || [];
        if (!note.tags.includes(t)) {
          note.tags.push(t);
          note.updatedAt = Date.now();
          persist();
          renderEditorTags(note);
          renderNoteList();
          renderTags();
          toast('Tag added', 'success');
        }
      }
      closeModal('tagModal');
    });
    document.getElementById('tagInput').addEventListener('keydown', (e) => {
      if (e.key === 'Enter') document.getElementById('tagSave').click();
    });

    // Close modals on overlay click
    document.querySelectorAll('.modal-overlay').forEach(ov => {
      ov.addEventListener('click', (e) => {
        if (e.target === ov) ov.classList.remove('open');
      });
    });

    // Command palette
    document.getElementById('cmdOverlay').addEventListener('click', (e) => {
      if (e.target.id === 'cmdOverlay') closeCmd();
    });
    document.getElementById('cmdInput').addEventListener('input', (e) => {
      renderCmdResults(e.target.value);
    });
    document.getElementById('cmdInput').addEventListener('keydown', (e) => {
      const results = document.getElementById('cmdResults');
      const items = results._items || [];
      if (e.key === 'ArrowDown') {
        e.preventDefault();
        results._hi = Math.min((results._hi || 0) + 1, items.length - 1);
        results.querySelectorAll('.cmd-item').forEach((el, i) => el.classList.toggle('highlighted', i === results._hi));
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        results._hi = Math.max((results._hi || 0) - 1, 0);
        results.querySelectorAll('.cmd-item').forEach((el, i) => el.classList.toggle('highlighted', i === results._hi));
      } else if (e.key === 'Enter') {
        e.preventDefault();
        runHighlightedCmd();
      } else if (e.key === 'Escape') {
        closeCmd();
      }
    });
    document.getElementById('cmdResults').addEventListener('click', (e) => {
      const item = e.target.closest('[data-cmd-idx]');
      if (!item) return;
      const items = document.getElementById('cmdResults')._items || [];
      const idx = +item.dataset.cmdIdx;
      if (items[idx]) items[idx].action();
    });

    // Context menu actions
    document.getElementById('contextMenu').addEventListener('click', (e) => {
      const item = e.target.closest('[data-ctx]');
      if (!item) return;
      const menu = document.getElementById('contextMenu');
      const fn = menu._actions && menu._actions[item.dataset.ctx];
      hideContextMenu();
      if (fn) fn();
    });
    document.addEventListener('click', hideContextMenu);
    document.addEventListener('scroll', hideContextMenu, true);

    // Global shortcuts
    document.addEventListener('keydown', (e) => {
      // Don't capture when typing in inputs except our intentional shortcuts
      const inField = e.target.matches('input, textarea') || e.target.isContentEditable;

      if (e.key === 'Escape') {
        closeCmd();
        document.querySelectorAll('.modal-overlay.open').forEach(m => m.classList.remove('open'));
        if (state.focusMode) toggleFocus();
        return;
      }

      if (isMod(e) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        openCmd();
        return;
      }

      if (isMod(e) && e.key.toLowerCase() === 'n' && !e.shiftKey) {
        e.preventDefault();
        createNote();
        return;
      }

      if (isMod(e) && e.key === '.') {
        e.preventDefault();
        toggleFocus();
        return;
      }

      // Formatting shortcuts when in content
      if (inField && e.target.id === 'noteContent') {
        if (isMod(e) && e.key.toLowerCase() === 'b') {
          e.preventDefault();
          document.execCommand('bold');
          updateNote(contentEl.dataset.noteId, { content: contentEl.innerHTML });
        }
        if (isMod(e) && e.key.toLowerCase() === 'i') {
          e.preventDefault();
          document.execCommand('italic');
          updateNote(contentEl.dataset.noteId, { content: contentEl.innerHTML });
        }
        if (isMod(e) && e.key.toLowerCase() === 'u') {
          e.preventDefault();
          document.execCommand('underline');
          updateNote(contentEl.dataset.noteId, { content: contentEl.innerHTML });
        }
      }
    });

    // Responsive back button visibility
    function updateMobileUI() {
      const back = document.getElementById('btnBackList');
      back.style.display = window.innerWidth <= 640 ? '' : 'none';
    }
    window.addEventListener('resize', updateMobileUI);
    updateMobileUI();

    // Prefer dark if system wants it and no stored preference on first run
    if (!localStorage.getItem(STORAGE_KEY) && window.matchMedia('(prefers-color-scheme: dark)').matches) {
      state.theme = 'dark';
    }
  }

  // ─── Init ───
  load();
  bindEvents();
  fullRender();
})();
</script>
</body>
</html>