/* =========================================================
   NomadPortal — dark terminal-aesthetic stylesheet
   ========================================================= */

/* Roboto Mono Nerd Font — bundled to match MeshChat's renderer exactly.
 * Box-drawing glyphs fully span the em-box (adjacent rows touch at
 * line-height: 1 / normal without sub-pixel gaps), and the font's own
 * kerning brings Braille glyphs (U+2800-28FF) flush so a row of
 * full-dot Braille reads as a contiguous grid. Don't disable kerning
 * on Micron content (`#page-content *`) or the Braille gap returns. */
@font-face {
  font-family: 'Roboto Mono Nerd Font';
  src: url('/static/fonts/RobotoMonoNerdFont/RobotoMonoNerdFont-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

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

:root {
  --bg:         #131313;
  --bg2:        #1c1c1c;
  --bg3:        #252525;
  --border:     #333;
  --text:       #c8c8c8;
  --text-dim:   #666;
  --accent:     #5ba3c9;
  --accent2:    #7ec8a0;
  --warn:       #c8905b;
  --error:      #c85b5b;
  --font-mono:  "Roboto Mono Nerd Font", "Cascadia Code", "Fira Code",
                "JetBrains Mono", "Consolas", "Courier New", monospace;
  --font-ui:    system-ui, -apple-system, "Segoe UI", "Roboto",
                "Helvetica Neue", Arial, sans-serif;
  --topbar-h:   42px;
  --statusbar-h:24px;
  --sidebar-w:  260px;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 13px;
  line-height: 1.5;
  /* 100vh is the LARGEST possible viewport on mobile — it ignores the
     on-screen keyboard, so opening it (e.g. to type a reply) shrank the
     visible area without the body resizing, pushing #chat-input and the
     tail of the conversation down behind the keyboard. 100dvh tracks the
     actual visible viewport and updates live as the keyboard opens/closes;
     the 100vh line stays first as a fallback for browsers that don't
     support dvh (the later declaration wins where it's supported). */
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ---- Disclaimer banner ---- */
#disclaimer-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  background: #1a1500;
  border-bottom: 1px solid #554400;
  color: #c8b86a;
  font-size: 11px;
  padding: 6px 14px;
  line-height: 1.5;
}
#disclaimer-banner[hidden] { display: none; }
#disclaimer-banner button {
  background: none;
  border: 1px solid #554400;
  color: #c8b86a;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 2px;
  flex-shrink: 0;
}
#disclaimer-banner button:hover { background: #2a2000; }

/* ---- Scrollbar ---- */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--bg2); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* =========================================================
   Top bar
   ========================================================= */
#topbar {
  height: var(--topbar-h);
  background: var(--bg2);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  flex-shrink: 0;
  z-index: 10;
}

.brand {
  color: var(--accent);
  font-weight: bold;
  white-space: nowrap;
  font-size: 14px;
  user-select: none;
}

#nav-bar {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
}
#nav-bar[hidden] { display: none; }

#nav-bar button {
  background: var(--bg3);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 3px 8px;
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  border-radius: 2px;
}
#nav-bar button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
#nav-bar button:disabled { opacity: 0.35; cursor: default; }

#address-bar {
  flex: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: inherit;
  font-size: 12px;
  padding: 4px 8px;
  border-radius: 2px;
  outline: none;
}
#address-bar:focus { border-color: var(--accent); }

#btn-go {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--bg);
  padding: 3px 12px;
  cursor: pointer;
  font-family: inherit;
  font-weight: bold;
  border-radius: 2px;
}
#btn-go:hover { background: #7ec8e3; }

#status-indicator {
  font-size: 10px;
  flex-shrink: 0;
}
.status-idle  { color: var(--text-dim); }
.status-ok    { color: var(--accent2); }
.status-busy  { color: var(--warn); }
.status-error { color: var(--error); }

/* =========================================================
   Main layout
   ========================================================= */
#layout {
  display: flex;
  flex: 1;
  overflow: hidden;
}

/* =========================================================
   Sidebar
   ========================================================= */
#sidebar {
  width: var(--sidebar-w);
  background: var(--bg2);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  overflow: hidden;
}

/* Sidebar panel switcher (Nodes / Messages). ``:not([hidden])`` on
   the display rule so an id selector's specificity doesn't beat the
   HTML ``hidden`` attribute — same class of bug that made the
   fingerprint icon visible for guests in v1.0.0. This element is
   set hidden by applyUISettings when the audience is guest-locked
   out of both sidebar panels. */
#sidebar-tabs:not([hidden]) {
  display: flex;
  flex-shrink: 0;
  background: var(--bg3);
  border-bottom: 1px solid var(--border);
}
.sidebar-tab {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-dim);
  padding: 7px 4px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.sidebar-tab:hover { color: var(--text); }
.sidebar-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

#sidebar-panel-nodes,
#sidebar-panel-messages {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
#sidebar[hidden] { display: none; }
#sidebar-panel-nodes[hidden],
#sidebar-panel-messages[hidden] { display: none; }

.sidebar-header {
  padding: 8px 10px;
  background: var(--bg3);
  border-bottom: 1px solid var(--border);
  color: var(--accent2);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.icon-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 14px;
  padding: 0 2px;
  line-height: 1;
}
.icon-btn:hover { color: var(--accent); }
.icon-btn.fav-active { color: var(--warn); }
#btn-fav-page { font-size: 18px; padding: 0 6px; }
/* ``display: inline-flex`` on the id selector otherwise wins over
   the HTML ``hidden`` attribute (which sets ``display: none`` via
   the browser's default stylesheet with lower specificity), leaving
   the fingerprint button visible for guests who shouldn't see it.
   Scope the display rule to ``:not([hidden])`` so the hidden
   attribute actually hides. Same class of bug lives elsewhere in
   this file wherever an id selector sets ``display`` on an element
   that also toggles ``hidden`` — worth auditing on the next pass. */
#btn-identify:not([hidden]) { padding: 0 6px; display: inline-flex; align-items: center; }
#btn-identify svg { display: block; }
#btn-identify.identify-active { color: var(--accent); }

#node-filter-wrap {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}
#node-filter {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: inherit;
  font-size: 12px;
  padding: 3px 6px;
  border-radius: 2px;
  outline: none;
}
#node-filter:focus { border-color: var(--accent); }

#node-list {
  list-style: none;
  flex: 1;
  overflow-y: auto;
  padding: 4px 0;
}

#node-list li {
  padding: 7px 12px;
  cursor: pointer;
  border-left: 2px solid transparent;
}
/* Node/favorite rows only (section headers and the placeholder row
   don't carry data-hash) become a real flex row: .node-icon-row and
   .node-right are both flex items now, not a float. A float let a long
   node name visually overlap and intercept clicks meant for the star
   button — the row's overflow:hidden clipped the *paint*, but the
   float's reserved space wasn't reliably excluded from the nowrap
   text's hit-testing box in every layout path (real repro: a long-
   named favorited node whose star could never be clicked, every tap
   landing on the row's own "navigate" handler instead). .node-right
   keeps order:2 so it still renders on the right despite being first
   in the DOM (see makeNodeItem's own comment for why it's inserted
   first). */
#node-list li[data-hash] { display: flex; align-items: flex-start; gap: 6px; }
#node-list li:hover  { background: var(--bg3); border-left-color: var(--accent); }
#node-list li.active { background: var(--bg3); border-left-color: var(--accent2); color: var(--accent2); }
/* Identicon + name/hash/age stack. flex:1 + min-width:0 is what lets it
   shrink and actually hand its unclaimed space to .node-right instead
   of pushing/overlapping it. */
#node-list .node-icon-row { display: flex; align-items: flex-start; gap: 7px; flex: 1; min-width: 0; }
#node-list .node-identicon { margin-top: 1px; flex-shrink: 0; }
#node-list .node-text { min-width: 0; flex: 1; overflow: hidden; }
#node-list .node-name  {
  display: block; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#node-list .node-hash  {
  display: block; font-size: 10px; color: var(--text-dim); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#node-list .node-age   { display: block; font-size: 10px; color: var(--text-dim); }
#node-list .node-hops  {
  display: block;
  font-size: 10px;
  color: var(--accent2);
  margin-top: 2px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}
#node-list .node-hops-unknown { color: var(--text-dim); font-style: italic; }
.node-dot          { font-size: 10px; margin-right: 5px; vertical-align: middle; }
.node-dot-none     { color: var(--border); }
.node-dot-ok       { color: var(--accent); }
.node-dot-degraded { color: var(--warn); }
.node-dot-err      { color: var(--error); }

#node-list .node-section-header {
  padding: 4px 10px 2px;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  cursor: default;
  border-top: 1px solid var(--border);
  margin-top: 2px;
  user-select: none;
}
#node-list .node-section-header:first-child {
  border-top: none;
  margin-top: 0;
}
#node-list .node-section-header:hover {
  background: transparent;
  border-left-color: transparent;
}
#node-list .node-section-header.collapsible {
  cursor: pointer;
}
#node-list .node-section-header.collapsible:hover {
  color: var(--text);
  background: var(--bg3);
}
.section-toggle {
  display: inline-block;
  width: 12px;
  margin-right: 3px;
  text-align: center;
  font-size: 12px;
}

/* Right-side stack: star (or pin) on top, hops badge below it. A fixed-
   width flex item now (not float:right — see li[data-hash]'s own
   comment above), order:2 so it still lands on the right despite being
   first in the DOM. flex-shrink:0 guarantees it never gets squeezed by
   a long node name — it always has its own reserved, clickable space. */
#node-list .node-right {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  order: 2;
  margin-left: 6px;
}

.node-fav-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 18px;
  padding: 0;
  line-height: 1;
  color: #777;
}
.node-fav-btn.fav-active { color: var(--warn); }
.node-fav-btn:hover { color: var(--warn); }

.node-placeholder { color: var(--text-dim); font-style: italic; font-size: 12px; }

/* "Load N more" sentinel row — see _makeLoadMoreRow() in app.js. Shared
   by the node list and the LXMF peer (Users tab) list. */
.list-load-more {
  color: var(--text-dim);
  font-size: 11px;
  text-align: center;
  padding: 8px 12px;
  cursor: pointer;
  user-select: none;
}
.list-load-more:hover { color: var(--text); background: var(--bg3); }

#node-list li.node-locked { opacity: 0.35; cursor: default; }
#node-list li.node-locked:hover { background: transparent; }

#sidebar-footer {
  padding: 5px 10px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-dim);
}

/* =========================================================
   Content area
   ========================================================= */
#content-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}

/* Breadcrumb + diagnostics strip above #page-content. Inconspicuous
 * by design — it should give "where am I, what's the network state"
 * info without competing with the page itself for attention. */
#page-breadcrumb {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--bg2);
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
}
#page-breadcrumb[hidden] { display: none; }
.bc-node {
  color: var(--text);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: 0;
}
.bc-sep { color: var(--text-dim); opacity: 0.5; }
.bc-path {
  font-family: inherit;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: 0;
}
.bc-spacer { flex: 1; }
.bc-chip {
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 1px 6px;
  font-size: 10px;
  color: var(--text-dim);
  white-space: nowrap;
}
.bc-chip[hidden] { display: none; }
.bc-btn {
  background: var(--bg3);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 2px 8px;
  font-family: inherit;
  font-size: 11px;
  border-radius: 2px;
  cursor: pointer;
}
.bc-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.bc-btn:disabled { opacity: 0.4; cursor: default; }
.bc-btn[hidden] { display: none; }

#node-sort {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: inherit;
  font-size: 11px;
  padding: 2px 4px;
  margin-top: 4px;
  width: 100%;
  cursor: pointer;
}

/* Breadcrumb-embedded select (auto-refresh dropdown). Smaller than
 * .bc-btn so it sits unobtrusively next to the chip row. */
.bc-select {
  background: var(--bg3);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 11px;
  padding: 1px 4px;
  border-radius: 2px;
  cursor: pointer;
}
.bc-select:focus { color: var(--text); border-color: var(--accent); outline: none; }

/* When the operator picks a reading-friendly view, page content
 * switches from `white-space: pre` (preserves ASCII art column
 * alignment) to `pre-wrap` (long prose lines wrap inside the column).
 * Toggle lives in the topbar Raw row. */
#page-content.wrap-prose .mu-line,
#page-content.wrap-prose .mu-page {
  white-space: pre-wrap;
}

#topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  font-size: 12px;
  margin-left: auto;
}
.raw-toggle { display: flex; align-items: center; gap: 4px; cursor: pointer; color: var(--text-dim); }
.raw-toggle input { cursor: pointer; accent-color: var(--accent); }


/* Force Roboto Mono Nerd Font with no fallbacks for the Micron content area
 * — the bundled font ships with Micron2HTML and has the box-drawing metrics
 * we rely on. monospace is a last-resort fallback if the font fails to load. */
#page-content,
#page-content * {
  font-family: "Roboto Mono Nerd Font", monospace;
  /* We only ship the Regular weight of the font. Without `font-synthesis`
   * disabled, Firefox synthesizes bold by thickening regular glyphs which
   * subtly widens characters and breaks monospace alignment in centered
   * lines that mix bold/non-bold spans. */
  font-synthesis-weight: none;
  /* Kerning + ligatures both off. Roboto Mono Nerd Font carries kerning
   * pairs and a few ligatures that subtly shave fractional cell widths
   * off adjacent glyphs — invisible on short text, but in dense
   * box-drawing / ASCII-art content (the geomap world map, frame art,
   * etc.) the fractions accumulate across a row and visibly offset the
   * stacking of rows below. MeshChat renders without kerning, so this
   * is required for parity. (The earlier Braille-glyph kerning
   * dependency is gone — Braille is now CSS-drawn via radial-gradient
   * dots in `.mu-braille`, fully font-independent.) */
  font-kerning: none;
  font-feature-settings: "kern" 0, "liga" 0;
}

/* Fake-bold via `text-shadow`: drawing the glyph offset 0.5px to the right
 * of itself thickens it visually without changing the layout box (so
 * monospace alignment is preserved in centered lines). Applies to every
 * <strong> the Micron2HTML converter emits for `! tokens. */
#page-content strong {
  font-weight: normal;
  text-shadow: 0.5px 0 0 currentColor;
}

/* Braille rendering — fully CSS-drawn, font-independent. After Micron2HTML
 * renders the page, app.js's `_braillify()` walks the DOM and replaces
 * each Braille character (U+2800-28FF) with one of these spans, with the
 * raised dots encoded as a radial-gradient list in `--mu-braille-dots`.
 *
 * `width: 1ch` makes the cell match the surrounding monospace cell width
 * exactly (1ch = advance width of '0' in the inherited font), so a row
 * of full-dot Braille reads as a contiguous grid. Empty positions are
 * not drawn (no indicator dots), matching MeshChat's convention of only
 * showing raised dots. */
#page-content .mu-braille {
  display: inline-block;
  width: 1ch;
  height: 1em;
  vertical-align: baseline;
  position: relative;
  /* Hold a non-breaking space inside so the inline-box gets the correct
   * line-height even on lines that are entirely Braille. */
}
#page-content .mu-braille::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--mu-braille-dots, transparent);
  background-repeat: no-repeat;
}

#page-content {
  flex: 1;
  overflow: auto;
  /* MeshChat parity: uniform 12px padding (Tailwind `p-3`) on the container.
   * All inner elements have NO horizontal padding so text is flush with the
   * container's content edge — and heading bgs span exactly the same width
   * as the body text rows (no 1-char grey strip beside heading text).
   *
   * NomadPortal-specific (intentional deviation from MeshChat): never wrap
   * Micron lines. Long lines extend off the right and the container shows
   * a horizontal scrollbar — preserving box-drawing alignment, ASCII art,
   * and code blocks that would otherwise break when wrapped. */
  padding: 12px;
  min-width: 0;
  background: #000000;
  color: #dddddd;
  white-space: pre;
  line-height: normal;
  font-size: 16.64px;
}

#page-error {
  margin: 20px 28px;
  padding: 12px 16px;
  background: #2a1010;
  border: 1px solid var(--error);
  color: var(--error);
  border-radius: 2px;
}

/* ---- Welcome screen ---- */
#welcome { color: var(--text-dim); max-width: 560px; margin: 40px auto; text-align: center; }
#welcome .welcome-art { color: var(--accent); margin-bottom: 20px; line-height: 1.4; }
#welcome p { margin-bottom: 8px; }
#welcome .muted { font-size: 11px; color: var(--text-dim); margin-top: 16px; }

/* ---- Loading overlay ---- */
#loading-overlay {
  position: absolute;
  inset: 0;
  background: rgba(19,19,19,0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  color: var(--accent);
  gap: 10px;
}
#loading-overlay[hidden] { display: none; }
.spinner {
  animation: blink 0.8s step-end infinite;
}
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }

/* =========================================================
   Status bar
   ========================================================= */
#statusbar {
  height: var(--statusbar-h);
  background: var(--bg2);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 10px;
  gap: 16px;
  font-size: 11px;
  color: var(--text-dim);
  flex-shrink: 0;
}
#cache-info { margin-left: auto; }

/* =========================================================
   Micron-rendered content — MeshChat-parity rendering.
   Reference: liamcottle/reticulum-meshchat → src/frontend/js/MicronParser.js
   ========================================================= */
/* MeshChat ignores `#!bg=` / `#!fg=` document headers (treats # lines as
 * comments). We override Micron2HTML's `.mu-page` inline-style wrapper so the
 * page bg/fg always come from `#page-content`, matching MeshChat. */
.mu-page {
  padding: 0;
  min-height: 100%;
  background: transparent !important;
  color: inherit !important;
}

/* ---- Section headings — match MeshChat's dark-theme defaults ----
 * Source: liamcottle/reticulum-meshchat → src/frontend/js/MicronParser.js
 *   STYLES_DARK.heading1 = { fg: "222", bg: "bbb" }
 *   STYLES_DARK.heading2 = { fg: "111", bg: "999" }
 *   STYLES_DARK.heading3 = { fg: "000", bg: "777" }
 * Author-supplied `Fxxx` flows through and overrides the default fg via the
 * inline `style="color:..."` that the converter emits for inner spans. */
.mu-h1, .mu-h2, .mu-h3 {
  display: block;
  font-family: var(--font-mono);
  font-weight: bold;
  /* `margin: 0` matches MeshChat's Tailwind preflight reset — its headings
   * have no vertical spacing of their own; the only gap comes from the
   * surrounding blank lines in the source.
   *
   * `margin-bottom: -1.08em` matches `.mu-line` so the next element doesn't
   * sit on top of an extra empty row from Roboto Mono Nerd Font's inflated
   * line metrics. */
  margin: 0 0 -1.08em 0;
  white-space: pre;
}
.mu-h1 { background: #bbbbbb; color: #222222; }
.mu-h2 { background: #999999; color: #111111; }
.mu-h3 { background: #777777; color: #000000; }

/* ---- Regular lines & blanks — match MeshChat's `<pre>` wrapping behaviour:
 *      preserve whitespace inside a line but wrap at viewport width, and
 *      give blank lines a full line-height so they read like real blank rows
 *      (MeshChat emits `<br>` for blanks; we use a div so we size manually). */
/* Each Micron line is its own `<div>`. Roboto Mono Nerd Font has inflated
 * vertical metrics (extra leading for the Nerd Font icon glyphs), so even
 * at `line-height: 1` rows leave a small gap that breaks the box-drawing
 * characters (║, │, ─). Forcing a small negative top margin pulls each line
 * up enough to overlap and visually connect the box-drawing rows. */
.mu-line  {
  display: block;
  /* `-1.08em` scales with the font-size (was tuned at 13px → -14px = ~1.08em).
   * Keeps box-drawing rows touching at any font-size. */
  margin: 0 0 -1.08em 0;
  line-height: 1;
  /* `white-space: pre` — never wrap; long lines scroll horizontally via the
   * container (NomadPortal viewer choice, not Micron parity). */
  white-space: pre;
}
.mu-blank { display: block; line-height: 1; height: 1em; margin-bottom: -1.08em; }
.mu-h1, .mu-h2, .mu-h3 { line-height: 1.2; padding: 2px 0; }
pre.mu-literal { line-height: 1; }

/* ---- Dividers ----
 * MeshChat parity:
 *   `-`  / `--`  → plain thin horizontal line (browser-default <hr>)
 *   `-=` / `=-`  → row of `=` characters
 *   `-X`          → row of X (the source char)
 *
 * `.mu-hr-double` uses hardcoded `::before` content because the `<hr>`
 * element has no text content.  `.mu-divider` uses `text-shadow` to repeat
 * whatever single character the converter emitted. Both share the body
 * text colour so they don't look dimmer than surrounding paragraph text. */
/* Dividers align with body text — no extra horizontal padding (the
 * 12px container padding on #page-content already keeps them inset
 * from the screen edge). */
.mu-hr {
  display: block;
  border: 0;
  border-top: 1px solid currentColor;
  /* `-1.08em` matches `.mu-line` to remove the empty row that the inflated
   * Nerd Font metrics would otherwise leave below this divider. */
  margin: 0 0 -1.08em 0;
  height: 0;
}
.mu-hr-double {
  display: block;
  border: 0;
  margin: 0 0 -1.08em 0;
  height: 1em;
  overflow: hidden;
  white-space: nowrap;
  color: inherit;
  font-family: inherit;
}
.mu-hr-double::before {
  content: "================================================================================================================================================================";
}
.mu-divider {
  display: block;
  margin: 0 0 -1.08em 0;
  overflow: hidden;
  white-space: nowrap;
  color: inherit;
  text-shadow:
    1ch 0, 2ch 0, 3ch 0, 4ch 0, 5ch 0, 6ch 0, 7ch 0, 8ch 0, 9ch 0, 10ch 0,
    11ch 0, 12ch 0, 13ch 0, 14ch 0, 15ch 0, 16ch 0, 17ch 0, 18ch 0, 19ch 0, 20ch 0,
    21ch 0, 22ch 0, 23ch 0, 24ch 0, 25ch 0, 26ch 0, 27ch 0, 28ch 0, 29ch 0, 30ch 0,
    31ch 0, 32ch 0, 33ch 0, 34ch 0, 35ch 0, 36ch 0, 37ch 0, 38ch 0, 39ch 0, 40ch 0,
    41ch 0, 42ch 0, 43ch 0, 44ch 0, 45ch 0, 46ch 0, 47ch 0, 48ch 0, 49ch 0, 50ch 0,
    51ch 0, 52ch 0, 53ch 0, 54ch 0, 55ch 0, 56ch 0, 57ch 0, 58ch 0, 59ch 0, 60ch 0,
    61ch 0, 62ch 0, 63ch 0, 64ch 0, 65ch 0, 66ch 0, 67ch 0, 68ch 0, 69ch 0, 70ch 0,
    71ch 0, 72ch 0, 73ch 0, 74ch 0, 75ch 0, 76ch 0, 77ch 0, 78ch 0, 79ch 0, 80ch 0,
    81ch 0, 82ch 0, 83ch 0, 84ch 0, 85ch 0, 86ch 0, 87ch 0, 88ch 0, 89ch 0, 90ch 0,
    91ch 0, 92ch 0, 93ch 0, 94ch 0, 95ch 0, 96ch 0, 97ch 0, 98ch 0, 99ch 0, 100ch 0,
    101ch 0, 102ch 0, 103ch 0, 104ch 0, 105ch 0, 106ch 0, 107ch 0, 108ch 0, 109ch 0, 110ch 0,
    111ch 0, 112ch 0, 113ch 0, 114ch 0, 115ch 0, 116ch 0, 117ch 0, 118ch 0, 119ch 0, 120ch 0,
    121ch 0, 122ch 0, 123ch 0, 124ch 0, 125ch 0, 126ch 0, 127ch 0, 128ch 0, 129ch 0, 130ch 0,
    131ch 0, 132ch 0, 133ch 0, 134ch 0, 135ch 0, 136ch 0, 137ch 0, 138ch 0, 139ch 0, 140ch 0,
    141ch 0, 142ch 0, 143ch 0, 144ch 0, 145ch 0, 146ch 0, 147ch 0, 148ch 0, 149ch 0, 150ch 0;
}

/* MeshChat parity: line-level bg from a leading `B` token shouldn't fill
 * the whole row. Micron2HTML emits `style="background-color:..."` on the
 * `.mu-line` wrapper to mimic terminal behaviour; force it transparent so
 * only the explicit `<span style="background-color:...">` segments show
 * the bg colour, matching MeshChat's parser. */
#page-content .mu-line {
  background-color: transparent !important;
}

/* ---- Links ----
 * MeshChat parity: links take whatever colour is active in the parser state
 * (the author's `Fxxx` if any, or the plain default #ddd). They are not
 * underlined by default; hover adds an underline. We mirror that with
 * `color: inherit` so the surrounding span colour flows through. */
a.mu-link {
  color: inherit;
  text-decoration: none;
}
a.mu-link:hover { text-decoration: underline; }

a.mu-dynamic {
  color: var(--warn);
  font-size: 11px;
  text-decoration: none;
  border: 1px solid var(--border);
  padding: 0 3px;
  border-radius: 2px;
}

/* ---- Inline formatting ---- */
.mu-ul { text-decoration: underline; }

/* ---- Form fields ----
 * MeshChat parity: text inputs are white background with mid-grey text
 * (matches the disabled-input look that MeshChat ships out of the box).
 * Padding bumped from default browser metrics so they read at body size. */
input.mu-field {
  background: #ffffff;
  border: 1px solid var(--border);
  color: #bababa;
  font-family: var(--font-mono);
  font-size: inherit;
  padding: 6px 8px;
  border-radius: 2px;
  cursor: not-allowed;
}
input.mu-field:not([disabled]) {
  color: #bababa;
  cursor: text;
  outline: none;
}
input.mu-field:not([disabled]):focus {
  border-color: var(--accent);
}
input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
  cursor: pointer;
  margin: 0 4px 0 0;
}
input[type="checkbox"][disabled], input[type="radio"][disabled] {
  cursor: not-allowed;
  opacity: 0.5;
}

/* ---- Literal / code blocks ----
 * MeshChat parity: literal blocks have NO border, bg, or padding — they
 * flow inline with surrounding text using the same font/colour. The boxed
 * "code-block" appearance is a NomadPortal-specific embellishment we drop
 * here. Whitespace preservation comes from `<pre>` itself.
 *
 * Selector includes `#page-content` so it beats the `#page-content pre`
 * rule (lower in this file, used for the "Raw Micron view") on specificity. */
#page-content pre.mu-literal {
  white-space: pre;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  border-radius: 0;
}

/* Raw Micron view */
#page-content pre {
  white-space: pre-wrap;
  word-break: break-word;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
}

/* =========================================================
   Chat / Conversations
   ========================================================= */

/* tab-chats needs its own flex layout so the two sub-views fill correctly */
#tab-chats { overflow: hidden; }

#chat-list-view, #chat-view {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
#chat-list-view[hidden], #chat-view[hidden] { display: none; }

#chat-list-inner { flex: 1; overflow-y: auto; }

#chat-list-footer {
  flex-shrink: 0;
  padding: 6px 10px;
  border-top: 1px solid var(--border);
}
#btn-compose-new {
  width: 100%;
  background: var(--bg3);
  border: 1px solid var(--accent);
  color: var(--accent);
  padding: 5px;
  font-family: var(--font-mono);
  font-size: 12px;
  cursor: pointer;
  border-radius: 2px;
}
#btn-compose-new:hover { background: #1a2a35; }

/* Conversation list items */
.conv-item {
  padding: 7px 10px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.conv-item:hover { background: var(--bg3); }
.conv-item.conv-unread .conv-name { font-weight: bold; color: var(--text); }
.conv-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 2px;
}
.conv-name { font-size: 12px; color: var(--text-dim); }
.conv-time { font-size: 10px; color: var(--text-dim); flex-shrink: 0; margin-left: 4px; }
.conv-preview {
  font-size: 11px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conv-you { color: var(--accent); }

/* Chat header (back btn + name) */
#chat-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--bg3);
  flex-shrink: 0;
}
#chat-contact-name {
  flex: 1;
  font-size: 12px;
  color: var(--accent2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Chat log (the scrollable bubble area) */
#chat-log {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

/* Inline compose area */
#chat-compose {
  flex-shrink: 0;
  border-top: 1px solid var(--border);
  padding: 6px 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#chat-compose select {
  font-size: 11px;
  padding: 2px 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--font-mono);
  border-radius: 2px;
  outline: none;
}
.chat-input-row { display: flex; gap: 4px; align-items: flex-end; }
/* Hidden until the compose box gets close to the server's 64 KB cap on
   /api/messages content (routes.py) — no point cluttering the box for
   every two-line reply, but silently failing/rejecting a message that
   size someone actually typed (or pasted) needs a visible reason. */
#chat-char-count {
  font-size: 10px;
  color: var(--text-dim);
  text-align: right;
}
#chat-char-count.chat-char-count-warn { color: var(--warn); }
#chat-char-count.chat-char-count-over { color: var(--error); }
#chat-input {
  flex: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  padding: 4px 6px;
  border-radius: 2px;
  outline: none;
  resize: none;
  line-height: 1.4;
}
#chat-input:focus { border-color: var(--accent); }
#btn-chat-send {
  background: var(--accent);
  border: none;
  color: var(--bg);
  padding: 4px 9px;
  border-radius: 2px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 14px;
  align-self: flex-end;
  line-height: 1;
}
#btn-chat-send:hover { background: #7ec8e3; }
#btn-chat-send:disabled { opacity: 0.4; cursor: default; }

/* Chat bubbles */
.chat-bubble {
  max-width: 88%;
  padding: 5px 9px;
  border-radius: 10px;
  word-break: break-word;
  font-size: 12px;
  line-height: 1.4;
}
.chat-bubble-received {
  background: var(--bg3);
  border: 1px solid var(--border);
  align-self: flex-start;
  border-bottom-left-radius: 2px;
}
.chat-bubble-sent {
  background: #173040;
  border: 1px solid #2a5570;
  align-self: flex-end;
  border-bottom-right-radius: 2px;
}
.chat-title {
  font-size: 10px;
  color: var(--text-dim);
  font-style: italic;
  margin-bottom: 2px;
}
.chat-content { white-space: pre-wrap; }
.chat-meta {
  font-size: 10px;
  color: var(--text-dim);
  margin-top: 3px;
  text-align: right;
}
.chat-state-ok   { color: var(--accent2); }
.chat-state-fail { color: var(--error); }
.chat-state-pend { color: var(--warn); }

/* =========================================================
   Mobile / narrow-screen layout
   ========================================================= */
@media (max-width: 640px) {
  :root { --sidebar-w: 100%; }

  /* Topbar: let items wrap and shrink */
  #topbar { flex-wrap: wrap; height: auto; padding: 6px 8px; gap: 6px; }
  #nav-bar { order: 2; width: 100%; }
  #topbar-actions { order: 3; }
  #status-indicator { order: 4; }
  #user-area { order: 5; border-left: none; padding-left: 0; }

  /* Sidebar overlays content on mobile, full-page — capping it to 85%/300px
     left a sliver of the underlying page visible and made the node/message
     list cramped on phone-width screens. */
  #layout { position: relative; }
  #sidebar {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    z-index: 20;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    width: 100%;
    max-width: none;
  }
  #sidebar.mobile-open { transform: translateX(0); box-shadow: 4px 0 16px rgba(0,0,0,.5); }

  /* Content fills full width */
  #content-wrap { width: 100%; }

  /* Sidebar toggle button (injected by JS) */
  #btn-sidebar-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg3);
    border: 1px solid var(--border);
    color: var(--text);
    width: 32px; height: 32px;
    cursor: pointer;
    font-size: 16px;
    border-radius: 2px;
    flex-shrink: 0;
  }

  /* Mobile overlay backdrop */
  #sidebar-backdrop {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0,0,0,.4);
    z-index: 19;
  }
  #sidebar-backdrop.visible { display: block; }

  /* While a conversation is open, the "Announce identity" block and the
     Chats/Users tab bar above it are dead weight — both are flex-shrink: 0
     siblings of #tab-chats, so they never gave way to #chat-log even when
     the keyboard shrank the available height, squeezing the conversation
     itself down to a sliver. Hide them for the duration; openConversation()
     adds .chat-open, the back/delete handlers remove it. */
  #sidebar-panel-messages.chat-open #announce-section,
  #sidebar-panel-messages.chat-open #msg-tabs {
    display: none;
  }

  /* Admin panel — stack nav vertically */
  #admin-topbar { flex-wrap: wrap; height: auto; padding: 8px; gap: 0; }
  .admin-brand  { border-right: none; border-bottom: 1px solid var(--border);
                  width: 100%; padding: 6px 0; margin-bottom: 4px; }
  nav.admin-nav { flex-wrap: wrap; width: 100%; }
  nav.admin-nav a { padding: 6px 10px; font-size: 12px; }
  .admin-user { margin-left: 0; border-left: none; padding-left: 0;
                border-top: 1px solid var(--border); width: 100%; padding-top: 6px; }
  #admin-content { padding: 16px; }
}
