/* The menu component is its own file so the landing page can load it
   without the rest; @import has to be the first rule. */
@import url("menu.css");

/* ==========================================================================
   lint.uz — shared components
   Depends on theme.css for all tokens.
   ========================================================================== */

* { box-sizing: border-box; }
/* hidden means hidden, even on an element whose own rule sets a display */
[hidden] { display: none !important; }
html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--ui);
  font-size: var(--fs-base);
  background: var(--surface);
  color: var(--ink);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-thumb {
  background: var(--scroll); border-radius: var(--radius);
  border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background-clip: content-box; background-color: var(--line-strong); }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }

::selection { background: color-mix(in srgb, var(--hue) 28%, transparent); }

/* the ring follows each element's own radius, so none is forced here */
:focus-visible {
  outline: 2px solid var(--hue);
  outline-offset: 1px;
}
/* with more contrast the hue may be too close to the surface to see, so the
   ring is drawn in the ink, thicker and further out */
[data-contrast="more"] :focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}

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

/* ==========================================================================
   Toolbar
   ========================================================================== */
.toolbar {
  display: flex; align-items: center; gap: var(--s1);
  padding: var(--s2) var(--s3);
  background: var(--raised);
  border-bottom: var(--hairline) solid var(--line);
  flex: none;
}

/* The lint.one mark, like the Apple menu: the suite's glyph in the ink,
   the one icon at the start of every toolbar. It opens the suite menu
   (app.js). */
.suite-wrap { margin-right: 0; }
.suite-btn {
  width: 28px; height: 28px; padding: 0;
  display: grid; place-items: center;
}
.suite-mark {
  width: 22px; height: 22px; flex: none;
  /* the favicon's corner, 15 of 64, at this size */
  border-radius: calc(22px * 15 / 64);
  background: var(--ink);
  opacity: 0.82;
  transition: opacity var(--t-fast);
}
.suite-btn:hover .suite-mark, .suite-btn[aria-expanded="true"] .suite-mark { opacity: 1; }
.suite-mark::before {
  content: ""; display: block; width: 100%; height: 100%;
  background: var(--surface);
  -webkit-mask: url("/shared/glyph.svg") center / 100% no-repeat;
  mask: url("/shared/glyph.svg") center / 100% no-repeat;
}
/* it is the first thing in the toolbar, so its menu opens rightwards */
.suite-menu { left: 0; right: auto; min-width: 220px; }

/* Brand: the tool's name, bold, after the lint.one mark — the way a Mac
   menu bar shows the  menu and then the app, with no icon between them;
   the format's tile is the favicon's job. Not a link: the mark goes home. */
.brand {
  display: flex; align-items: center; gap: var(--s2);
  margin-right: var(--s3); padding-right: var(--s3);
  border-right: var(--hairline) solid var(--line);
  text-decoration: none; color: inherit; user-select: none;
  flex: none;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name {
  font-size: var(--fs-mid); font-weight: 600; letter-spacing: -0.01em;
}

/* The open document, right after Open: its name and the way to close it.
   Quiet — a sunken chip, not a button — because it is a fact about the
   page, with one action on its edge. */
.doc-chip {
  display: inline-flex; align-items: center; gap: 2px;
  min-width: 0; flex: 0 1 auto;
  margin-left: var(--s1);
  padding: 0 2px 0 var(--s2);
  height: 28px;
  background: var(--sunken);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  color: var(--muted);
}
.doc-name {
  max-width: 28ch; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-small); font-weight: 500; color: var(--ink);
}
.doc-close {
  width: 22px; height: 22px; padding: 0;
  display: grid; place-items: center; flex: none;
  color: var(--muted); border-radius: var(--radius-sm);
}
.doc-close:hover { color: var(--ink); }
.doc-close svg { width: 13px; height: 13px; display: block; }

/* Buttons */
button {
  font-family: var(--ui);
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: var(--hairline) solid transparent;
  border-radius: var(--radius);
  padding: 5px 10px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
button:hover { background: var(--row-hover); }
button:active { background: var(--row-active); }
button[disabled] { opacity: 0.4; cursor: not-allowed; }
button.bordered { border-color: var(--line); }
button.primary {
  background: var(--hue); color: var(--hue-ink);
  border-color: var(--hue); font-weight: 600;
}
button.primary:hover { background: color-mix(in srgb, var(--hue) 86%, var(--ink)); }
button.primary:active { background: color-mix(in srgb, var(--hue) 76%, var(--ink)); }
button[aria-pressed="true"] {
  background: var(--hue-wash); color: var(--hue);
  border-color: color-mix(in srgb, var(--hue) 40%, transparent);
  font-weight: 600;
}

.group { display: flex; align-items: center; gap: 2px; }

.sep { width: var(--hairline); height: 18px; background: var(--line); margin: 0 var(--s2); flex: none; }
.spacer { flex: 1 1 auto; min-width: var(--s2); }

/* Icon-only buttons */
.icon-btn {
  width: 30px; height: 28px; padding: 0;
  display: grid; place-items: center;
  color: var(--muted);
}
.icon-btn:hover { color: var(--ink); }
.icon-btn svg { width: 15px; height: 15px; display: block; }

/* Segmented control — one choice out of a few, shown as positions.
   Every tool uses this one; the chosen segment is raised, never hued. */
.seg {
  display: inline-flex; gap: 2px; padding: 2px; flex: none;
  background: var(--sunken);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
}
.seg button { padding: 3px 10px; font-size: var(--fs-small); color: var(--muted); border-radius: var(--radius-sm); }
.seg button:hover { color: var(--ink); }
.seg button[aria-pressed="true"], .seg button[aria-checked="true"] {
  background: var(--raised); color: var(--ink);
  border-color: transparent; font-weight: 600;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
}
.seg button svg { width: 14px; height: 14px; display: block; }

kbd {
  font-family: var(--mono); font-size: var(--fs-micro);
  background: var(--sunken); border: var(--hairline) solid var(--line);
  border-radius: var(--radius-sm); padding: 1px 5px; color: var(--muted);
}

/* View tabs (mobile) — the same shape as .seg */
.tabs {
  display: none; gap: 2px; padding: 2px; flex: none;
  background: var(--sunken);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
}
.tabs button { padding: 4px 13px; border-radius: var(--radius-sm); color: var(--muted); font-size: var(--fs-small); }
.tabs button.active { background: var(--raised); color: var(--ink); font-weight: 600; }

/* ==========================================================================
   Menus — the component itself lives in menu.css, imported above
   ========================================================================== */

/* Suite switcher: each tool as a tiny app icon, its glyph on its hue.
   The paths are absolute because a url() inside a custom property resolves
   where it is used, not in this file. */
.dot-json  { background: #4F46E5; --tile-glyph: url("/shared/glyphs/json.svg"); }
.dot-xml   { background: #0F766E; --tile-glyph: url("/shared/glyphs/xml.svg"); }
.dot-yaml  { background: #B45309; --tile-glyph: url("/shared/glyphs/yaml.svg"); }
.dot-csv   { background: #4D7C0F; --tile-glyph: url("/shared/glyphs/csv.svg"); }
.dot-pdf   { background: #BE123C; --tile-glyph: url("/shared/glyphs/pdf.svg"); }
.dot-log   { background: #0369A1; --tile-glyph: url("/shared/glyphs/log.svg"); }
.dot-audio { background: #C026D3; --tile-glyph: url("/shared/glyphs/audio.svg"); }
.dot-sqlite { background: #7C3AED; --tile-glyph: url("/shared/glyphs/sqlite.svg"); }
/* saffron is the one light hue: its glyph is ink */
.dot-parquet { background: #F7CE46; --tile-glyph: url("/shared/glyphs/parquet.svg"); --tile-ink: #1A1D23; }
.dot-env   { background: #A32972; --tile-glyph: url("/shared/glyphs/env.svg"); }
.dot-har   { background: #475569; --tile-glyph: url("/shared/glyphs/har.svg"); }
.dot-cert  { background: #4ADE80; --tile-glyph: url("/shared/glyphs/cert.svg"); --tile-ink: #1A1D23; }
.tool-tile { width: 16px; height: 16px; border-radius: var(--radius-sm); flex: none; }
.tool-tile::before {
  content: ""; display: block; width: 100%; height: 100%;
  background: var(--tile-ink, #FFFFFF);
  -webkit-mask: var(--tile-glyph) center / 100% no-repeat;
  mask: var(--tile-glyph) center / 100% no-repeat;
}

/* ==========================================================================
   Split layout
   ========================================================================== */
.split { flex: 1; display: flex; min-height: 0; }
.pane { display: flex; flex-direction: column; min-height: 0; }

#editorPane { width: 46%; min-width: 260px; background: var(--raised); position: relative; }
#treePane { flex: 1; min-width: 0; background: var(--surface); }

#divider {
  width: 6px; flex: none; cursor: col-resize;
  background: var(--surface);
  border-left: var(--hairline) solid var(--line);
  border-right: var(--hairline) solid var(--line);
  position: relative;
}
#divider::after {
  content: ""; position: absolute; inset: 0;
  background: var(--hue); opacity: 0;
  transition: opacity var(--t-fast);
}
#divider:hover::after, #divider.dragging::after { opacity: 1; }

/* ==========================================================================
   Editor — gutter + highlight overlay + textarea, all sharing one grid
   ========================================================================== */
.editor {
  flex: 1; position: relative; min-height: 0;
  display: grid;
  grid-template-columns: var(--gutter-w) 1fr;
  overflow: hidden;
  /* every layer below inherits these — they MUST stay in lockstep. The size
     is the reader's text size (LintApp.textZoom), never the chrome's. */
  --ed-font: var(--code-size, var(--fs-base))/1.6 var(--mono);
  --ed-pad-y: var(--s3);
  --ed-pad-x: var(--s3);
}

/* The gutter carries the tool's hue — a faint wash and a hued edge, so a
   tab is recognisable without the gutter being the loudest thing on screen */
.gutter {
  position: relative;
  overflow: hidden;
  background: color-mix(in srgb, var(--hue) var(--gutter-tint), var(--raised));
  border-right: 2px solid color-mix(in srgb, var(--hue) 55%, var(--line));
  font: var(--ed-font);
  color: color-mix(in srgb, var(--hue) var(--gutter-ink), var(--muted));
  text-align: right;
  user-select: none;
  padding: var(--ed-pad-y) var(--s2) var(--ed-pad-y) 0;
  font-variant-numeric: tabular-nums;
  font-size: calc(var(--fs-small) * var(--code-scale, 1));
}
.gutter-inner { will-change: transform; }
/* one number per line of text, so its height follows the text's size */
.gutter .ln { display: block; height: calc(var(--code-size, var(--fs-base)) * 1.6); }
/* a number with a message on it can be pointed at or tapped */
.gutter .ln[data-tip] { cursor: help; }
/* the current line is marked by its number alone; an error stays a filled
   block, because an error should be loud */
.gutter .ln.cur {
  color: var(--gutter-cur);
  font-weight: 600;
}
.gutter .ln.warn {
  color: var(--warn);
  font-weight: 700;
  box-shadow: inset -3px 0 0 var(--warn);
  margin-right: calc(var(--s2) * -1);
  padding-right: var(--s2);
}
/* a marked line's message, shown beside its number on hover */
.gutter-tip {
  position: fixed; z-index: 60; max-width: min(56ch, 70vw);
  padding: var(--s1) var(--s2);
  background: var(--raised); color: var(--ink);
  border: var(--hairline) solid var(--line-strong); border-left: 3px solid var(--warn);
  border-radius: var(--radius); box-shadow: var(--shadow);
  font: 500 var(--fs-small)/1.45 var(--ui); white-space: pre-line;
  pointer-events: none;
}
.gutter-tip.err { border-left-color: var(--bad); }
.gutter .ln.err {
  color: var(--hue-ink);
  background: var(--bad);
  font-weight: 700;
  margin-right: calc(var(--s2) * -1);
  padding-right: var(--s2);
}

/* Text layers: highlight underneath, transparent textarea on top */
.text-layers { position: relative; overflow: hidden; }

#highlight, #input {
  position: absolute; inset: 0;
  margin: 0; border: 0;
  padding: var(--ed-pad-y) var(--ed-pad-x);
  font: var(--ed-font);
  letter-spacing: 0;
  tab-size: 2;
  white-space: pre;
  overflow-wrap: normal;
  word-break: normal;
}
#highlight {
  overflow: auto;
  pointer-events: none;
  color: var(--ink);
  scrollbar-width: none;
}
#highlight::-webkit-scrollbar { width: 0; height: 0; }
#input {
  overflow: auto;
  resize: none;
  background: transparent;
  color: transparent;
  caret-color: var(--hue);
  -webkit-text-fill-color: transparent;
}
#input::selection { background: color-mix(in srgb, var(--hue) 30%, transparent); }
#input:focus { outline: none; }
#input::placeholder {
  color: var(--faint);
  -webkit-text-fill-color: var(--faint);
}
.editor:focus-within .gutter {
  background: color-mix(in srgb, var(--hue) calc(var(--gutter-tint) + 2%), var(--raised));
}

/* Syntax tokens inside the highlight layer */
.hl-key   { color: var(--t-key); }
.hl-str   { color: var(--t-str); }
.hl-num   { color: var(--t-num); }
.hl-bool  { color: var(--t-bool); font-weight: 600; }
.hl-null  { color: var(--t-null); font-style: italic; }
.hl-punct { color: var(--t-punct); }
.hl-tag   { color: var(--t-key); }
.hl-attr  { color: var(--t-num); }
.hl-cmt   { color: var(--faint); font-style: italic; }
.hl-anchor{ color: var(--t-cont); }
.hl-doc   { color: var(--t-cont); font-weight: 600; }
.hl-err   { text-decoration: underline wavy var(--bad); text-underline-offset: 3px; }

/* Error bar — uses --bad, never the brand hue */
#errorBar {
  display: none; align-items: baseline; gap: var(--s2);
  flex: none;
  padding: var(--s2) var(--s3);
  font-family: var(--mono); font-size: var(--fs-small);
  color: var(--bad);
  background: color-mix(in srgb, var(--bad) 9%, var(--raised));
  border-top: var(--hairline) solid color-mix(in srgb, var(--bad) 30%, transparent);
  cursor: pointer;
  text-align: left;
  width: 100%;
  border-radius: 0;
}
#errorBar.show { display: flex; }
#errorBar:hover { background: color-mix(in srgb, var(--bad) 15%, var(--raised)); }
#errorBar .emsg { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#errorBar .loc {
  color: var(--bad); opacity: 0.8; text-decoration: underline;
  text-underline-offset: 2px; white-space: nowrap; flex: none;
}

/* ==========================================================================
   Tree pane
   ========================================================================== */
.treebar {
  display: flex; align-items: center; gap: var(--s1);
  flex: none;
  padding: var(--s2) var(--s3);
  border-bottom: var(--hairline) solid var(--line);
  background: var(--raised);
}
.search-wrap { flex: 1; min-width: 70px; position: relative; display: flex; align-items: center; }
/* the icon is centred against the input, not the zero-height span that
   wraps it — anchor it to the .search-wrap box instead */
.search-wrap svg {
  position: absolute; left: 9px; top: 50%;
  transform: translateY(-50%);
  width: 13px; height: 13px;
  color: var(--faint); pointer-events: none;
}
#search {
  width: 100%;
  font-family: var(--ui); font-size: var(--fs-base);
  color: var(--ink);
  background: var(--sunken);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 5px var(--s2) 5px 27px;
}
#search::placeholder { color: var(--faint); }
#search:focus { background: var(--raised); }
#search::-webkit-search-cancel-button { filter: grayscale(1); opacity: 0.5; }

#matchCount {
  font-size: var(--fs-small); color: var(--muted);
  white-space: nowrap; font-variant-numeric: tabular-nums;
  padding: 0 var(--s1);
}
.treebar .icon-btn { width: 28px; height: 26px; }
.step-nav { display: inline-flex; }

#tree {
  flex: 1; overflow: auto;
  font-family: var(--mono); font-size: var(--code-size, var(--fs-base)); line-height: 1.55;
  padding: var(--s2) var(--s3) 40vh var(--s2);
  scroll-padding-block: var(--s5);
}
#tree.stale { opacity: 0.4; }

.empty {
  padding: var(--s6) var(--s4);
  font-family: var(--ui); color: var(--faint);
  max-width: 46ch; line-height: 1.6;
}

/* A heading, one sentence and at most a row of buttons. Built by
   LintApp.emptyState for the tools without an editor (LOG, PDF, Audio) and
   for later messages in the tree. It matches the editor tools' prompt below
   in type and in height (measured against it, for a container that starts
   under the toolbar), so moving between tools the first screen does not
   jump. */
.empty-state {
  margin: calc(40vh - 37px) auto 0;
  padding: 0 var(--s4) var(--s5);
  max-width: 50ch; text-align: center;
  font-family: var(--ui); color: var(--muted);
}
.empty-state h2 {
  margin: 0;
  font-size: var(--fs-display); font-weight: 600;
  letter-spacing: -0.015em; line-height: 1.25; color: var(--ink);
  text-wrap: balance;
}
.empty-state p { margin: var(--s2) 0 0; font-size: var(--fs-mid); line-height: 1.5; text-wrap: balance; }
.empty-state .empty-actions {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s2); margin: var(--s5) 0 0;
}
.empty-state .keys { font-size: var(--fs-small); color: var(--faint); margin: 0; }

.children {
  margin-left: 15px; padding-left: var(--s2);
  border-left: var(--hairline) solid var(--line);
  display: none;
}
.node.open > .children { display: block; }
.children:hover { border-left-color: color-mix(in srgb, var(--hue) 45%, var(--line)); }

.row {
  display: flex; align-items: baseline;
  padding: 1px var(--s2) 1px 2px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  position: relative;
  cursor: default;
}
.row:hover { background: var(--row-hover); }
.row.selected { background: var(--row-active); }
/* the match being stepped to, marked apart from the others it highlights */
.row.current-hit { background: var(--hue-wash); box-shadow: inset 2px 0 0 var(--hue); }

/* the row menu opens where the pointer is, not under a button */
.ctx-menu { position: fixed; right: auto; top: 0; left: 0; min-width: 220px; }

.caret {
  flex: none; width: 15px; align-self: center;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--faint); cursor: pointer;
  transition: transform var(--t-fast);
}
.caret::before {
  content: ""; display: block;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
}
.node.open > .row .caret { transform: rotate(90deg); }
.caret.leaf { visibility: hidden; cursor: default; }

.ticon {
  flex: none; width: 17px; height: 15px; border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  align-self: center; margin-right: var(--s2);
  /* the one size off the scale: two letters inside a 15px chip */
  font-family: var(--mono); font-size: calc(var(--fs-micro) - 2px); font-weight: 700;
  letter-spacing: -0.05em; user-select: none;
}
.t-obj  { color: var(--t-key);  background: color-mix(in srgb, var(--t-key) 15%, transparent); }
.t-arr  { color: var(--t-cont); background: color-mix(in srgb, var(--t-cont) 15%, transparent); }
.t-str  { color: var(--t-str);  background: color-mix(in srgb, var(--t-str) 15%, transparent); }
.t-num  { color: var(--t-num);  background: color-mix(in srgb, var(--t-num) 15%, transparent); }
.t-bool { color: var(--t-bool); background: color-mix(in srgb, var(--t-bool) 15%, transparent); }
.t-null { color: var(--t-null); background: color-mix(in srgb, var(--t-null) 15%, transparent); }
.t-date { color: var(--t-date); background: color-mix(in srgb, var(--t-date) 15%, transparent); }
.t-attr { color: var(--t-num);  background: color-mix(in srgb, var(--t-num) 15%, transparent); }
.t-text { color: var(--t-str);  background: color-mix(in srgb, var(--t-str) 15%, transparent); }
.t-cmt  { color: var(--faint);  background: color-mix(in srgb, var(--faint) 15%, transparent); }

/* a long value ends in an ellipsis at the pane's edge rather than running
   under it; Wrap shows the whole thing */
#tree:not(.wrap) .v { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.caret, .k, .colon, .badge { flex: none; }

.k { color: var(--t-key); }
.k.idx { color: var(--faint); }
.colon { color: var(--t-punct); margin-right: var(--s2); }
.v.str { color: var(--t-str); }
.v.num { color: var(--t-num); }
.v.bool { color: var(--t-bool); font-weight: 600; }
.v.null { color: var(--t-null); font-style: italic; }
.v.date { color: var(--t-date); }
.attr-key { color: var(--t-num); margin-left: var(--s2); }
.attr-eq { color: var(--t-punct); }
.attr-val { color: var(--t-str); }

.badge { color: var(--muted); }
.badge .cnt {
  color: var(--faint); font-family: var(--ui); font-size: var(--fs-small);
  margin-left: var(--s2); font-variant-numeric: tabular-nums;
}

mark {
  background: var(--mark); color: var(--mark-ink);
  border-radius: calc(var(--radius-sm) / 2); padding: 0 1px;
}

.more-btn, .trunc-btn {
  font-family: var(--ui); font-size: var(--fs-small);
  color: var(--hue); border: none; padding: 1px 6px;
  font-weight: 500;
}
.more-btn { margin: 2px 0 2px 20px; }
.more-btn:hover, .trunc-btn:hover { background: var(--hue-wash); }

.row-actions {
  display: none; gap: 1px;
  position: absolute; top: 0; right: 2px;
  background: inherit; border-radius: var(--radius-sm);
  padding: 0 2px 0 var(--s3);
}
.row:hover .row-actions, .row.selected .row-actions,
.row:focus-visible .row-actions { display: inline-flex; }
.row-actions::before {
  content: ""; position: absolute; left: -18px; top: 0; bottom: 0; width: 18px;
  background: linear-gradient(to right, transparent, var(--raised));
  pointer-events: none; opacity: 0;
}
.row-actions button {
  border: none; padding: 1px 6px;
  font-family: var(--ui); font-size: var(--fs-micro); font-weight: 500;
  color: var(--muted); border-radius: var(--radius-sm);
}
.row-actions button:hover { color: var(--hue); background: var(--hue-wash); }

.notice {
  color: var(--muted); font-family: var(--ui);
  font-size: var(--fs-small); padding: var(--s2);
}

/* Wrap mode */
#tree.wrap .row { white-space: normal; align-items: flex-start; }
#tree.wrap .v { white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; min-width: 0; }
#tree.wrap .k, #tree.wrap .colon, #tree.wrap .badge { flex: 0 0 auto; }
#tree.wrap .k { max-width: 100%; overflow-wrap: anywhere; }
#tree.wrap .caret { align-self: flex-start; margin-top: 4px; }
#tree.wrap .ticon { align-self: flex-start; margin-top: 2px; }

/* ==========================================================================
   Status bar
   ========================================================================== */
.status {
  display: flex; align-items: center; gap: var(--s3);
  flex: none;
  padding: var(--s1) var(--s3);
  min-height: 29px;
  background: var(--raised);
  border-top: var(--hairline) solid var(--line);
  font-size: var(--fs-small); color: var(--muted);
}
.status .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--faint); flex: none;
}
.status.ok .dot { background: var(--ok); }
.status.err .dot { background: var(--bad); }
.status.err #statusMsg { color: var(--bad); font-weight: 500; }
#statusMsg {
  white-space: nowrap; font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis;
}
/* facts sit apart by space alone; inline, so the line still ellipsizes */
#statusMsg > span + span { margin-left: var(--s3); }
#pathBox {
  font-family: var(--mono); font-size: var(--fs-small);
  flex: 1; min-width: 0; text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--muted); cursor: pointer;
  direction: rtl;
}
/* rtl only so a long path loses its start, not its end; the marks keep
   its punctuation in reading order — without them "$.a[0]" shows as "a[0].$" */
#pathBox:not(:empty)::before, #pathBox:not(:empty)::after { content: "\200E"; }
#pathBox:hover { color: var(--hue); }
#pathBox:empty { cursor: default; }

/* ==========================================================================
   Toast
   ========================================================================== */
#toast {
  position: fixed; left: 50%; bottom: 46px;
  transform: translateX(-50%) translateY(6px);
  background: var(--ink); color: var(--surface);
  font-size: var(--fs-base); font-weight: 500;
  padding: 7px var(--s4); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-mid), transform var(--t-mid);
  z-index: 100;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* only a toast offering an action may be clicked; plain ones stay inert */
#toast.actionable {
  display: flex; align-items: center; gap: var(--s3);
  padding-right: var(--s1);
}
#toast.actionable.show { pointer-events: auto; }
.toast-action {
  color: var(--surface);
  background: color-mix(in srgb, var(--surface) 18%, transparent);
  border-radius: var(--radius);
  padding: 4px var(--s3);
  font-size: var(--fs-small); font-weight: 600;
  text-decoration: none; white-space: nowrap;
  transition: background-color var(--t-fast);
}
.toast-action:hover { background: color-mix(in srgb, var(--surface) 30%, transparent); }
.toast-action:focus-visible { outline: 2px solid var(--surface); outline-offset: 1px; }

/* ==========================================================================
   Empty editor — body.is-empty (set by app.js while the editor is empty)
   The page is the editor and one calm prompt over it: no tree, no gutter,
   no status. The prompt ignores the pointer except on its buttons, so a
   click anywhere else lands in the textarea underneath.
   ========================================================================== */
body.is-empty #treePane,
body.is-empty #divider,
body.is-empty .gutter,
body.is-empty #errorBar,
body.is-empty #statusBar,
body.is-empty .tabs { display: none; }
/* !important beats the width the divider drag leaves inline */
body.is-empty #editorPane { width: 100% !important; }
body.is-empty .editor { grid-template-columns: 1fr; }
/* the prompt replaces the placeholder */
body.is-empty #input::placeholder { color: transparent; -webkit-text-fill-color: transparent; }

.empty-prompt {
  display: none;
  position: absolute; inset: 0; z-index: 4;
  align-items: center; justify-content: center;
  padding: var(--s5) var(--s4);
  text-align: center;
  pointer-events: none;
}
body.is-empty .empty-prompt { display: flex; }
/* nothing to format yet: the actions keep their place, so the toolbar does
   not shift on the first paste, but show only once they can do something.
   Hidden by visibility, not display, so the mobile overflow menu does not
   mistake them for folded-away actions. */
body.is-empty .tool-actions { visibility: hidden; }
/* sits a little above the true centre, where the eye expects it */
.empty-prompt-body { max-width: 56ch; margin-top: -10vh; text-wrap: balance; }
.empty-prompt h2 {
  margin: 0;
  font-size: var(--fs-display); font-weight: 600;
  letter-spacing: -0.015em; line-height: 1.25;
  color: var(--ink);
  transition: color var(--t-fast);
}
.empty-prompt .empty-line {
  margin: var(--s2) 0 0;
  font-size: var(--fs-mid); line-height: 1.5; color: var(--muted);
}
.empty-prompt.arrived .empty-line { display: none; }
.empty-actions {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s2); margin-top: var(--s5);
}
.empty-actions button { pointer-events: auto; padding: 7px var(--s4); }

/* arriving from another viewer: what is waiting, and the key that pastes it */
.empty-note {
  margin: var(--s3) auto 0;
  font-size: var(--fs-base); line-height: 1.55; color: var(--muted);
}
.empty-note p { margin: 0; }
.empty-note .arrived-lead { color: var(--ink); font-weight: 500; }

body.dragover #editorPane::after {
  content: "";
  position: absolute; inset: var(--s2);
  border: 2px dashed var(--hue);
  border-radius: var(--radius-lg);
  background: var(--hue-wash);
  pointer-events: none; z-index: 5;
}
body.dragover .empty-prompt h2 { color: var(--hue); }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 900px) {
  .hide-md { display: none; }
}

@media (max-width: 720px) {
  .tabs { display: inline-flex; }
  /* tighter tabs and icon buttons pay for the lint.one mark and the
     document's ×, so a 375px phone still fits the whole toolbar */
  .tabs button { padding: 4px 7px; }
  .toolbar .icon-btn { width: 26px; }
  #divider { display: none; }
  #editorPane { width: 100% !important; }
  body[data-view="tree"] #editorPane { display: none; }
  body[data-view="text"] #treePane { display: none; }
  .hide-sm { display: none; }
  /* a phone has no room for the separator: mark and name sit together */
  .suite-wrap { margin-right: 0; }
  .suite-btn { width: 24px; }
  .brand { border-right: 0; padding-right: 0; margin-right: 2px; }
  /* the name gives way first; the × stays so a phone can still close */
  .doc-name { display: none; }
  .doc-chip { flex: none; margin-left: 0; padding: 0; border: 0; background: transparent; }
  :root { --gutter-w: 40px; }
  .toolbar { gap: 2px; padding: var(--s2) 6px; }
}

/* ==========================================================================
   Keyboard shortcuts — LintApp.showShortcuts(), a native modal dialog
   ========================================================================== */
dialog.shortcuts {
  width: min(760px, calc(100vw - 32px));
  max-height: min(640px, calc(100dvh - 48px));
  padding: 0; overflow: auto;
  background: var(--raised); color: var(--ink);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  font-family: var(--ui);
}
dialog.shortcuts::backdrop { background: rgb(0 0 0 / 0.32); }
.sc-head {
  display: flex; align-items: center; gap: var(--s2);
  position: sticky; top: 0; z-index: 1;
  padding: var(--s3) var(--s3) var(--s3) var(--s5);
  background: var(--raised);
  border-bottom: var(--hairline) solid var(--line);
}
.sc-head h2 { margin: 0; flex: 1; font-size: var(--fs-large); font-weight: 600; letter-spacing: -0.01em; }
.sc-body { padding: var(--s4) var(--s5) var(--s5); columns: 2 300px; column-gap: var(--s6); }
.sc-group { break-inside: avoid; margin: 0 0 var(--s5); }
.sc-group h3 {
  margin: 0 0 var(--s2);
  font-size: var(--fs-micro); font-weight: 600; letter-spacing: 0.02em;
  color: var(--faint);
}
.sc-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
  padding: 5px 0;
  border-bottom: var(--hairline) solid var(--line);
  font-size: var(--fs-base);
}
.sc-row:last-child { border-bottom: 0; }
.sc-keys { flex: none; text-align: right; color: var(--faint); font-size: var(--fs-small); }
.sc-keys kbd { color: var(--ink); }

/* ==========================================================================
   Increase contrast — what the tokens alone cannot do: text drawn in the
   hue is not guaranteed legible on the surface, so it takes the ink
   ========================================================================== */
[data-contrast="more"] button[aria-pressed="true"] { color: var(--ink); border-color: var(--ink); }
[data-contrast="more"] .more-btn, [data-contrast="more"] .trunc-btn { color: var(--ink); text-decoration: underline; }
[data-contrast="more"] #divider { background: var(--line); }
[data-contrast="more"] .gutter { border-right-color: var(--ink); }
[data-contrast="more"] kbd { color: var(--ink); border-color: var(--line); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
  }
}
