/* 29Court Notebook — SHARP build (2026-10-20).
   The whole look rides Tide: tokens + primitives are vendored byte-true
   from Tide canon (repo-src/design-system) and loaded before this file.
   This file is host composition only: layout and the app's own surfaces,
   expressed entirely in Tide tokens. No hardcoded colors (build-checked). */
:root { color-scheme: light dark; }

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

#app { display: flex; flex-direction: column; height: 100dvh; }

/* ---------- top bar ---------- */
.topbar {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s4);
  background: var(--surface);
  border-bottom: 1px solid var(--edge);
  min-height: 56px;
}
.brand { display: flex; align-items: center; gap: var(--s2); font-family: var(--font-display); font-weight: 800; font-size: var(--fs-lg); letter-spacing: var(--track-head); white-space: nowrap; }
.brand .mark-img { width: 32px; height: 32px; display: block; }
.topbar .spacer { flex: 1; }
.sync-pill {
  /* The pill's words are the sync truth; they read at full ink, not
     muted (measured below the gate when muted, W1 At-Rest). */
  font-size: var(--fs-sm); color: var(--ink); font-weight: 600;
  background: var(--sunken); border-radius: var(--r-pill);
  padding: var(--s1) var(--s3); white-space: nowrap; cursor: pointer;
}

/* ---------- save banner (c29-banner host rules) ---------- */
c29-banner[hidden] { display: none !important; }
#saveAlert { margin: var(--s3) var(--s4) 0; }

/* ---------- buttons (host chrome; canon-owned controls fenced out) ---------- */
button, .court-btn {
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600;
  min-height: 44px; padding: 0 var(--s4);
  border-radius: var(--r-md);
  border: 1px solid var(--edge);
  background: var(--raised); color: var(--ink);
  cursor: pointer; display: inline-flex; align-items: center; gap: var(--s2);
  justify-content: center; white-space: nowrap;
}
button:hover { background: var(--surface); }
/* Primary actions use Tide's accent pair (fill + its on-* token),
   the canon pairing, AA in both themes by construction. */
button.primary, .court-btn.primary {
  background: var(--accent); border-color: var(--accent);
  color: var(--on-accent);
}
button.primary:hover { background: var(--accent); filter: brightness(0.94); }
button.icon { min-width: 44px; padding: 0 var(--s2); }
button.danger { color: var(--bad-text); border-color: var(--bad); }
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* Fences: controls Tide owns keep canon's own styling — the generic
   button rule above must not reach into c29-seg or c29-list rows. */
c29-seg button { min-height: 38px; padding: 0 14px; border: 0; background: none; border-radius: 99px; justify-content: center; }
c29-seg button:hover { background: none; }
c29-list [data-open] { border: 0; background: transparent; min-height: 0; padding: var(--s3) var(--s4); justify-content: stretch; display: grid; }
c29-list [data-open]:hover { background: transparent; }

/* ---------- layout ---------- */
.workspace { display: flex; flex: 1; min-height: 0; }
.sidebar {
  width: 300px; flex: 0 0 300px; display: flex; flex-direction: column;
  background: var(--bg); border-right: 1px solid var(--edge);
  min-height: 0; padding-top: var(--s3);
  /* Short screens: the rail scrolls as one piece rather than
     starving the note list (sections keep their natural height). */
  overflow-y: auto;
}
.sidebar > * { flex-shrink: 0; }
.sidebar > .note-list { flex-shrink: 1; }
.side-what { font-size: var(--fs-sm); line-height: 1.45; color: var(--ink-mute); margin: 0 var(--s3) var(--s2); }
.storage-line { font-size: var(--fs-sm); line-height: 1.45; color: var(--ink-mute); margin: 0 var(--s3) var(--s2); font-variant-numeric: tabular-nums; }
.storage-line.warn { color: var(--bad-text); font-weight: 700; }
.sidebar .side-search { padding: var(--s2) var(--s3) var(--s3); }
.side-search input, .court-input {
  width: 100%; min-height: 44px; padding: 0 var(--s3);
  border: 1px solid var(--29d-border-strong); border-radius: var(--r-md);
  background: var(--raised); color: var(--ink);
  /* iPhone Bar T-03: text-entry controls compute at 16px so iOS does
     not auto-zoom on focus. */
  font-family: inherit; font-size: 16px;
}
.side-search input:focus, .court-input:focus { border-color: var(--accent); outline: 2px solid var(--focus-ring); outline-offset: 1px; }

/* note rail: c29-list rows, host-tuned for rail density */
.note-list {
  flex: 1; min-height: 160px; overflow-y: auto; padding: 0 var(--s3) var(--s3);
  /* Rows keep their natural height inside the scroll region: without
     this, a height-squeezed grid row crushes the [data-open] grid's
     tracks and the row text clips to nothing (caught in the rig). */
  grid-auto-rows: max-content;
}
.note-list > li.is-current { background: var(--accent-soft); }
.note-row .t-item-title { font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-row .t-item-body { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.side-section { padding: var(--s2) var(--s3); border-top: 1px solid var(--edge); flex: none; }
.side-section h3 { margin: var(--s2) 0; font-size: var(--fs-cap); font-weight: 700; text-transform: uppercase; letter-spacing: var(--track-cap); color: var(--ink-mute); }
/* Sidebar section actions are compact chips in wrapping rows: the
   note list owns the sidebar's height; sections never squeeze it.
   (First sharp build made these full-width rows; five sections of
   stacked rows starved the list to 12px. Caught in the test rig.) */
.side-section button {
  width: auto; justify-content: flex-start; border: 1px solid var(--edge); background: var(--surface);
  border-radius: var(--r-pill); padding: var(--s1) var(--s3); margin: 0 var(--s1) var(--s1) 0;
  font-weight: 500; font-size: var(--fs-sm); color: var(--ink-soft); min-height: 36px;
}
.side-section button:hover { background: var(--wash); color: var(--ink); }
.tag-cloud { display: flex; flex-wrap: wrap; gap: var(--s1); }
.tag-chip {
  border: 1px solid var(--edge); background: var(--raised);
  border-radius: var(--r-pill); padding: var(--s1) var(--s3);
  font-size: var(--fs-sm); min-height: 36px; cursor: pointer; width: auto;
}
.tag-chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-text); }

/* open-source credits (moved from an inline <style>; Tide tokens now) */
.sidebar details.credits { margin-top: var(--s1); font-size: var(--fs-sm); line-height: 1.5; }
.sidebar details.credits summary { cursor: pointer; color: var(--accent-text); display: inline-block; padding: var(--s2) var(--s3); min-height: 40px; }
.sidebar .credits ul { list-style: none; margin: var(--s2) 0 0; padding: 0; text-align: left; }
.sidebar .credits li { display: flex; justify-content: space-between; gap: var(--s2); padding: 0; }
.sidebar .credits li em { font-style: normal; color: var(--ink-mute); white-space: nowrap; }
.sidebar .credits p { margin: 0 0 var(--s2); color: var(--ink-soft); }

/* ---------- editor area ---------- */
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.note-toolbar {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s4);
  border-bottom: 1px solid var(--edge); background: var(--surface);
  flex-wrap: wrap;
}
/* The title renders as the note's first line: same column, display
   type, no chrome of its own (notes grammar R7, adapted — the field
   stays because titles key the wikilinks). */
.title-input {
  flex: 1; min-width: 160px; border: 0; background: transparent;
  font-family: var(--font-display); font-size: var(--fs-2xl); font-weight: 800; letter-spacing: var(--track-head);
  color: var(--ink);
  min-height: 44px; padding: 0 var(--s2); border-radius: var(--r-md);
}
.title-input:focus { outline: 2px solid var(--focus-ring); }
.editor-wrap { flex: 1; display: flex; min-height: 0; position: relative; }
.pane { flex: 1; min-width: 0; min-height: 0; overflow-y: auto; display: none; }
.pane.show { display: block; }
.pane.editor.show { display: flex; justify-content: center; }
.pane.editor textarea {
  width: 100%; max-width: 44rem; height: 100%; border: 0; resize: none; background: var(--raised);
  /* iPhone Bar T-03: 16px floor for text entry. */
  color: var(--ink); font-family: var(--font-mono); font-size: 16px;
  line-height: 1.7; padding: var(--s6);
}
.pane.editor textarea:focus { outline: none; }
.pane.preview { padding: var(--s6); }
/* Readable measure: every rendered block caps at the same column the
   editor writes in (notes grammar R6). */
.pane.preview > * { max-width: 44rem; margin-left: auto; margin-right: auto; }

/* rendered markdown */
.preview { font-size: var(--fs-body); line-height: var(--lh-body); }
.preview h1 { font-size: var(--fs-3xl); } .preview h2 { font-size: var(--fs-2xl); }
.preview h3 { font-size: var(--fs-xl); } .preview h4 { font-size: var(--fs-lg); }
.preview h1, .preview h2, .preview h3, .preview h4 { font-family: var(--font-display); letter-spacing: var(--track-head); line-height: 1.25; margin: var(--s6) 0 var(--s3); }
.preview p { margin: var(--s3) 0; }
.preview a { color: var(--accent-text); }
.preview .wikilink { color: var(--accent-text); font-weight: 600; text-decoration: underline dotted; }
.preview .wikilink.missing { color: var(--bad-text); }
.preview code { font-family: var(--font-mono); background: var(--sunken); border-radius: var(--r-sm); padding: 0 var(--s1); font-size: 0.9em; }
.preview pre { background: var(--sunken); color: var(--ink); border-radius: var(--corner); padding: var(--s4); overflow-x: auto; }
.preview pre code { background: transparent; padding: 0; }
.preview blockquote { border-left: 4px solid var(--sun-500); margin: var(--s4) 0; padding: var(--s2) var(--s4); color: var(--ink-soft); background: var(--wash); border-radius: 0 var(--r-md) var(--r-md) 0; }
.preview table { border-collapse: collapse; width: 100%; margin: var(--s4) 0; }
.preview th, .preview td { border: 1px solid var(--edge); padding: var(--s2) var(--s3); text-align: left; }
.preview th { background: var(--sunken); }
.preview .table-wrap { overflow-x: auto; }
.preview ul.task { list-style: none; padding-left: var(--s2); }
.preview li.task input { width: 20px; height: 20px; accent-color: var(--accent); vertical-align: middle; }
.preview .hashtag { color: var(--accent-text); font-weight: 600; }
.preview img { max-width: 100%; border-radius: var(--r-md); }
.result-row mark, .wiki-suggest mark { background: color-mix(in srgb, var(--sun-300) 55%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }

/* ---------- right panel (links): closed by default, a toggle away ---------- */
.rightbar {
  display: none; width: 280px; flex: 0 0 280px; border-left: 1px solid var(--edge);
  background: var(--bg); overflow-y: auto; padding: var(--s3);
}
.rightbar.open { display: block; }
.rightbar h3 { font-size: var(--fs-cap); font-weight: 700; text-transform: uppercase; letter-spacing: var(--track-cap); color: var(--ink-mute); margin: var(--s4) 0 var(--s2); }
.rightbar h3:first-child { margin-top: 0; }
.link-row {
  display: block; width: 100%; text-align: left; background: var(--raised);
  border: 1px solid var(--edge); border-radius: var(--r-md);
  padding: var(--s2) var(--s3); margin-bottom: var(--s2); min-height: 44px;
  justify-content: flex-start; font-weight: 500;
}
.link-row:hover { border-color: var(--accent); background: var(--raised); }
.empty-note { color: var(--ink-mute); font-size: var(--fs-sm); }

/* ---------- modals (host-composed on Tide tokens; canon has no
   centered dialog — the gap is named in the record) ---------- */
.modal-backdrop {
  position: fixed; inset: 0; background: color-mix(in srgb, var(--ink) 45%, transparent);
  display: none; align-items: flex-start; justify-content: center; z-index: 50; padding: var(--s12) var(--s3) var(--s8);
}
.modal-backdrop.show { display: flex; }
.modal {
  background: var(--raised); border-radius: var(--corner);
  box-shadow: var(--lift-hi); width: 640px; max-width: 100%;
  max-height: 80dvh; display: flex; flex-direction: column; overflow: hidden;
}
.modal header { padding: var(--s4); border-bottom: 1px solid var(--edge); display: flex; align-items: center; gap: var(--s2); }
.modal header input { flex: 1; }
.modal .modal-body { overflow-y: auto; padding: var(--s2); }
.modal footer { padding: var(--s3) var(--s4); border-top: 1px solid var(--edge); display: flex; justify-content: flex-end; gap: var(--s2); }
.result-row { display: flex; gap: var(--s3); align-items: baseline; width: 100%; text-align: left; border: 0; background: transparent; border-radius: var(--r-md); padding: var(--s2) var(--s3); min-height: 44px; justify-content: flex-start; font-weight: 500; }
.result-row:hover, .result-row.sel { background: var(--accent-soft); }
.result-row .t { font-weight: 600; }
.result-row .m { font-size: var(--fs-sm); color: var(--ink-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kbd { font-family: var(--font-mono); font-size: var(--fs-sm); background: var(--sunken); border: 1px solid var(--edge); border-radius: var(--r-sm); padding: 0 var(--s1); }
.shortcut-table { width: 100%; border-collapse: collapse; }
.shortcut-table td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--edge); }
#graphCanvas { width: 100%; height: 60dvh; display: block; background: var(--bg); border-radius: var(--corner); }

/* wikilink autocomplete */
.wiki-suggest {
  position: absolute; z-index: 60; background: var(--raised);
  border: 1px solid var(--edge); border-radius: var(--corner);
  box-shadow: var(--lift-hi); max-height: 240px; overflow-y: auto; min-width: 240px; display: none;
}
.wiki-suggest.show { display: block; }

/* sync / share / history */
.ver-row { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) 0; border-bottom: 1px solid var(--edge); flex-wrap: wrap; }
.ver-label { flex: 1; min-width: 180px; font-variant-numeric: tabular-nums; font-size: var(--fs-sm); }
.fld { margin: var(--s3) 0; }
.fld label { display: block; font-weight: 600; font-size: var(--fs-sm); margin-bottom: var(--s1); }
.fld label.checkline { display: flex; align-items: center; gap: var(--s2); min-height: 44px; font-size: var(--fs-body); }
.fld label.checkline input { width: 22px; height: 22px; flex: none; }
.fld select.court-input { width: 100%; min-height: 44px; }
/* Platform29 helper surfaces (APPS 500 R13): gate prompt + resume bar. */
.p29-gate, .p29-resume {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  background: var(--raised); color: var(--ink);
  border-bottom: 1px solid var(--edge);
  font-size: var(--fs-sm);
}
.p29-gate-text, .p29-resume-text { flex: 1; min-width: 200px; margin: 0; }
.p29-gate a, .p29-resume a { color: var(--ink); font-weight: 700; text-decoration: underline; }
.modal .p29-gate, .modal .p29-resume { border: 1px solid var(--edge); border-radius: var(--r-md); margin-bottom: var(--s3); }
.share-h { margin: var(--s6) 0 var(--s2); font-size: var(--fs-lg); }
.share-h:first-child { margin-top: 0; }
.share-link-box { border: 1px solid var(--accent); background: var(--accent-soft); border-radius: var(--r-md); padding: var(--s3); display: flex; flex-direction: column; gap: var(--s2); margin: var(--s3) 0; }
.shared-owner { font-weight: 700; font-size: var(--fs-sm); margin: var(--s2) 0; color: var(--ink-mute); }

/* tracking controls: `-` checkboxes and `*` action buttons */
.preview li.task { margin: 6px 0; }
.preview li.task label { display: flex; gap: 10px; align-items: center; min-height: 44px; cursor: pointer; }
.preview li.task input { width: 22px; height: 22px; accent-color: var(--accent); flex: 0 0 auto; cursor: pointer; }
.preview li.task span.done { text-decoration: line-through; opacity: .6; }
.preview li.actbtn { list-style: none; margin: 8px 0; }
.el-btn { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 12px 20px; font-size: 15px; font-weight: 600; line-height: 1; color: var(--on-accent); background: var(--accent); border: 1px solid var(--accent); border-radius: var(--r-md); cursor: pointer; transition: filter var(--d2) var(--ease); }
.el-btn:hover, .el-btn:focus-visible { filter: brightness(1.1); }
.el-btn:active { filter: brightness(.92); }
.el-btn.is-done { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-text); }
.el-btn span.done { text-decoration: line-through; }

/* themes modal swatches (themes.js output) rides tokens via .t-item
   primitives; the picker cards themselves live in themes.css. */

/* mobile */
.menu-btn { display: none; }
@media (max-width: 900px) {
  .rightbar.open { position: fixed; right: 0; top: 0; bottom: 0; z-index: 40; box-shadow: var(--lift-hi); }
}
@media (max-width: 700px) {
  .menu-btn { display: inline-flex; }
  .sidebar { position: fixed; left: 0; top: 0; bottom: 0; z-index: 40; transform: translateX(-105%); transition: transform var(--d3) var(--ease); box-shadow: var(--lift-hi); visibility: hidden; }
  .sidebar.open { transform: none; visibility: visible; }
  .topbar #graphBtn, .topbar #infoBtn, .topbar #rightbarBtn, .topbar #themeBtn { display: none; }
  /* The sync pill stays visible on phones: its states are words a user
     must be able to read before trusting the notebook (W1 At-Rest). */
  .sync-pill { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; }
  .pane.preview { padding: var(--s4); }
  .pane.editor textarea { padding: var(--s4); }
  .brand .name { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* 29Court Notebook: theme system.
   NOTE: this file is intentionally exempt from the build.py "no hardcoded
   colors" check. Themes ARE color definitions: they override the --29c-*
   tokens with literal values. The app's own CSS (notebook.css) must still
   use tokens only. */

/* ---------------- built-in: light (explicit; matches the default tokens) ---------------- */
html[data-theme="light"] { color-scheme: light; }

/* ---------------- SHARP build: --29c aliases ride Tide ----------------
   The app's look now runs on Tide tokens. The --29c names survive only
   as aliases onto Tide, so this file's picker styles and any legacy
   consumer resolve through the ONE token source: Tide's ramps. Under
   [data-theme="dark"] these aliases pick up Tide's dark ramp for free,
   because var() resolves where it is used. */
:root {
  --29c-color-paper: var(--raised);
  --29c-color-sand-100: var(--bg);
  --29c-color-sand-200: var(--sunken);
  --29c-color-ink-950: var(--ink);
  --29c-color-ink-700: var(--ink-soft);
  --29c-color-ink-500: var(--ink-mute);
  --29c-color-ink-300: var(--ink-mute);
  --29c-color-hairline: var(--edge);
  --29c-color-primary: var(--accent);
  --29c-color-primary-strong: var(--accent-text);
  --29c-color-primary-soft: var(--accent-soft);
  --29c-color-primary-line: var(--accent);
  --29c-color-on-primary: var(--on-accent);
  --29c-color-info: var(--accent-text);
  --29c-color-info-soft: var(--accent-soft);
  --29c-color-sea: var(--accent);
  --29c-color-sea-deep: var(--accent-text);
  --29c-color-sea-soft: var(--accent-soft);
  --29c-color-success: var(--ok);
  --29c-color-success-deep: var(--ok-text);
  --29c-color-success-soft: var(--ok-bg);
  --29c-color-danger: var(--bad);
  --29c-color-danger-deep: var(--bad-text);
  --29c-color-danger-soft: var(--bad-bg);
  --29c-color-warning: var(--warn);
  --29c-color-warning-deep: var(--warn-text);
  --29c-color-warning-soft: var(--warn-bg);
  --29c-color-sunset: var(--sun-500);
  --29c-color-sunset-deep: var(--warn-text);
  --29c-color-sunset-soft: var(--warn-bg);
  --29c-color-gold: var(--sun-300);
  --29c-color-gold-soft: var(--wash);
  --29c-color-blush: var(--29d-coral-300);
  --29c-color-blush-soft: var(--warn-bg);
  --29c-input-border: var(--29d-border-strong);
  --29c-input-border-focus: var(--accent);
  --29c-ring: var(--focus);
  --29c-shadow-card: var(--lift);
  --29c-shadow-float: var(--lift-hi);
  --29c-shadow-pop: var(--lift-hi);
  --29c-space-1: var(--s1); --29c-space-2: var(--s2); --29c-space-3: var(--s3);
  --29c-space-4: var(--s4); --29c-space-6: var(--s6); --29c-space-8: var(--s8);
  --29c-text-sm: var(--fs-sm); --29c-text-base: var(--fs-body); --29c-text-lg: var(--fs-lg);
  --29c-text-xl: var(--fs-xl); --29c-text-2xl: var(--fs-2xl); --29c-text-3xl: var(--fs-3xl);
  --29c-radius-sm: var(--r-sm); --29c-radius-md: var(--r-md); --29c-radius-lg: var(--corner);
  --29c-radius-xl: var(--corner); --29c-radius-pill: var(--r-pill);
  --29c-font-sans: var(--font-ui); --29c-font-mono: var(--font-mono);
  --29c-leading-normal: var(--lh-body); --29c-leading-tight: 1.25; --29c-leading-loose: 1.7;
  --29c-motion-base: var(--d2); --29c-motion-ease: var(--ease);
}


/* ---------------- built-in: dark ----------------
   No literal palette anymore: dark IS Tide's own [data-theme="dark"]
   ramp in tokens.css, and everything here resolves through it. */
html[data-theme="dark"] { color-scheme: dark; }

/* ---------------- built-in: 29Court signature (ink and gold) ----------------
   A Tide theme the Tide way: re-point the ramps, keep the palette.
   Values are this theme's own (ink + gold on near-black warmth). */
html[data-theme="court"] {
  color-scheme: dark;
  --sand-0: #16120d;
  --sand-50: #1a150e;
  --sand-100: #1e1811;
  --sand-200: #2c2314;
  --sand-300: #3d3220;
  --sand-400: #6f5627;
  --ink-1: #f4eedd;
  --ink-2: #d9cdae;
  --ink-3: #a39372;
  --ink-4: #695d44;
  --sea-600: #d8a94e;
  --sea-700: #e9c06e;
  --on-accent: #191104;
  --ok-bg: #23402a; --ok-text: #93d897;
  --warn-bg: #4a3a1e; --warn-text: #f2c078;
  --bad-bg: #4a2727; --bad-text: #ee9d9d;
}
html[data-theme="court"] .preview pre { background: var(--sunken); color: var(--ink); }

/* ---------------- Obsidian bridge ----------------
   Maps the Obsidian color system onto the 29C tokens. Applies to the
   bundled Retroma theme and to any user-imported Obsidian theme, whose
   variables are injected at runtime under html[data-theme="obsidian"]. */

/* Fallback accent. Obsidian's host app normally provides --interactive-accent
   (the accent color the user picks in Obsidian's settings). Real Obsidian
   themes read it, and their whole variable chain collapses without it.
   Element-level specificity loses to any :root rule a theme ships, so a
   theme that sets its own accent always wins. */
html { --interactive-accent: #c65d7b; }

html[data-theme="obsidian"] body {
  --29c-color-paper: var(--background-primary);
  --29c-color-sand-100: var(--background-secondary);
  --29c-color-sand-200: var(--background-modifier-hover, var(--background-secondary-alt, var(--background-secondary)));
  --29c-color-hairline: var(--background-modifier-border);
  --29c-color-ink-950: var(--text-normal);
  --29c-color-ink-700: var(--text-normal);
  --29c-color-ink-500: var(--text-muted);
  --29c-color-ink-300: var(--text-faint, var(--text-muted));
  --29c-color-primary: var(--interactive-accent);
  --29c-color-primary-strong: var(--interactive-accent-hover, var(--interactive-accent));
  --29c-color-primary-soft: var(--background-modifier-hover);
  --29c-color-primary-line: var(--background-modifier-border-hover, var(--background-modifier-border));
  --29c-color-on-primary: var(--text-on-accent);
  --29c-color-info: var(--text-accent, var(--interactive-accent));
  --29c-color-info-soft: var(--background-modifier-hover);
  --29c-color-sea-deep: var(--text-accent, var(--interactive-accent));
  --29c-color-sunset-deep: var(--text-accent, var(--interactive-accent));
  --29c-color-gold: var(--text-accent, var(--interactive-accent));
  --29c-color-danger-deep: var(--text-error);
  --29c-input-border: var(--background-modifier-border);
  --29c-input-border-focus: var(--background-modifier-border-focus, var(--interactive-accent));
}
/* SHARP build: the same bridge, one vocabulary over — Tide semantic
   tokens mapped from the same Obsidian vars, so the Tide-based UI
   (list, seg, banner, editor) themes with the Obsidian theme too.
   Lives on body, at/below where Obsidian vars are defined (the
   substitution-scope lesson from THEMES.md). */
html[data-theme="obsidian"] body {
  --bg: var(--background-secondary);
  --surface: var(--background-primary);
  --raised: var(--background-primary);
  --sunken: var(--background-secondary-alt, var(--background-secondary));
  --ink: var(--text-normal);
  --ink-soft: var(--text-normal);
  --ink-mute: var(--text-muted);
  --edge: var(--background-modifier-border);
  --29d-border: var(--background-modifier-border);
  --29d-border-strong: var(--background-modifier-border-hover, var(--background-modifier-border));
  --accent: var(--interactive-accent);
  --accent-text: var(--text-accent, var(--interactive-accent));
  --accent-soft: var(--background-modifier-hover, var(--background-secondary));
  --on-accent: var(--text-on-accent);
}
html[data-theme="obsidian"].ob-dark body {
  --ok-bg: #23402a; --ok-text: #93d897;
  --warn-bg: #4a3a1e; --warn-text: #f2c078;
  --bad-bg: #4a2727; --bad-text: #ee9d9d;
}

html[data-theme="obsidian"].ob-dark { color-scheme: dark; }
html[data-theme="obsidian"].ob-light { color-scheme: light; }
html[data-theme="obsidian"] .preview pre {
  background: var(--code-background, var(--background-secondary));
  color: var(--code-normal, var(--text-normal));
}

/* ---------------- theme picker UI ---------------- */
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--29c-space-2); padding: var(--29c-space-2) var(--29c-space-4); }
.theme-card {
  border: 1px solid var(--29c-color-hairline); border-radius: var(--29c-radius-lg);
  background: var(--29c-color-paper); padding: var(--29c-space-3); text-align: left;
  display: flex; flex-direction: column; gap: var(--29c-space-2); min-height: 44px;
  cursor: pointer;
}
.theme-card:focus-visible { outline: 2px solid var(--29c-color-primary); outline-offset: 2px; }
.theme-card:hover { border-color: var(--29c-color-primary-line); }
.theme-card.active { border-color: var(--29c-color-primary); box-shadow: 0 0 0 2px var(--29c-color-primary-soft); }
.theme-card .swatches { display: flex; gap: 4px; }
.theme-card .swatches span { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--29c-color-hairline); }
.theme-card .t { font-weight: 700; font-size: var(--29c-text-base); color: var(--29c-color-ink-950); }
.theme-card .d { font-size: var(--29c-text-sm); color: var(--29c-color-ink-500); }
.theme-section { padding: var(--29c-space-2) var(--29c-space-4) 0; }
.theme-section h4 { margin: var(--29c-space-3) 0 var(--29c-space-1); font-size: var(--29c-text-sm); text-transform: uppercase; letter-spacing: 0.06em; color: var(--29c-color-ink-500); }
.theme-note { padding: var(--29c-space-2) var(--29c-space-4); color: var(--29c-color-ink-500); font-size: var(--29c-text-sm); }
.theme-import textarea {
  width: 100%; min-height: 110px; resize: vertical;
  font-family: var(--29c-font-mono); font-size: var(--29c-text-sm);
  border: 1px solid var(--29c-input-border); border-radius: var(--29c-radius-md);
  background: var(--29c-color-paper); color: var(--29c-color-ink-950);
  padding: var(--29c-space-2) var(--29c-space-3);
}
.theme-row { display: flex; gap: var(--29c-space-2); align-items: center; padding: var(--29c-space-2) var(--29c-space-4); flex-wrap: wrap; }
.theme-row input[type="text"] { flex: 1; min-width: 160px; }
.mode-toggle { display: inline-flex; border: 1px solid var(--29c-color-hairline); border-radius: var(--29c-radius-md); overflow: hidden; }
.mode-toggle button { border: 0; border-radius: 0; }
.mode-toggle button.on { background: var(--29c-color-ink-950); color: var(--29c-color-paper); }
.custom-theme-row { display: flex; align-items: center; gap: var(--29c-space-2); padding: var(--29c-space-2) var(--29c-space-4); }
.custom-theme-row .nm { flex: 1; font-weight: 600; color: var(--29c-color-ink-950); }
