/* txmesh shared theme — every page links this before its own <style>
   (as /theme.css?v=N — bump N in ALL pages when this file or theme.js
   changes; Cloudflare's zone browser-TTL overrides our no-cache for css/js).
   Single source of truth for design tokens, typography scale, and the
   shared components (topbar, buttons, panels, badges, tables, footer).
   Pages keep only page-specific rules locally and may override any token.

   Theme selection: html[data-theme] (stamped pre-paint by /theme.js from
   localStorage 'ms.theme') wins; with no stored choice the OS preference
   applies; the default is dark. */

/* ---------- design tokens ---------- */
:root{
  --bg:#0b0d10; --surface:#14171c; --surface-2:#1b2028; --hover:#1f2630;
  --border:rgba(255,255,255,.08); --border-2:rgba(255,255,255,.14);
  /* The ink ramp is spaced by perceived lightness (L*93 / 74 / 61 here) and
     floored at 4.5:1 against the LIGHTEST surface it lands on — --hover, the
     table-row hover, not the panel it usually sits on. --ink-3 used to be
     #606c7c: 2.85:1 there, well under the body-text floor, and it carries
     .muted, .sec-sub, .ptitle .sub, .foot, table headers and KPI labels —
     all of it text someone is meant to read. --ink-2 moved up with it to
     keep three distinguishable steps rather than two. */
  --ink:#e8edf3; --ink-2:#adb7c4; --ink-3:#8995a4;
  --accent:#22d3ee; --accent-dim:rgba(34,211,238,.13); --accent-contrast:#062a30;
  --good:#2ec26e; --warn:#fab219; --crit:#e15b5b;
  /* readable on-tint text for badges/status pills */
  --good-ink:#8ef0b6; --warn-ink:#ffd486; --crit-ink:#f3a3a3; --accent-ink:#8fd8e8;
  /* validated categorical ramp (charts, node types) */
  --c1:#3987e5; --c2:#199e70; --c3:#c98500; --c4:#008300;
  --c5:#9085e9; --c6:#e66767; --c7:#d55181; --c8:#d95926;
  --grid:rgba(255,255,255,.045); --axis:rgba(255,255,255,.10);
  --map-bg:#0a0c0f; --shadow:rgba(0,0,0,.5);
  --topbar-bg:rgba(11,13,16,.72);
  --glow-1:rgba(34,211,238,.09); --glow-2:rgba(34,211,238,.05);
  --font-sans:'Inter',system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  /* bootstrap bridge (no-ops on non-bootstrap pages) */
  --bs-body-bg:var(--bg); --bs-body-color:var(--ink);
  --bs-border-color:var(--border); --bs-emphasis-color:var(--ink);
  --bs-secondary-color:var(--ink-2); --bs-tertiary-color:var(--ink-3);
  --bs-font-sans-serif:var(--font-sans);
}
html[data-theme="light"]{
  --bg:#f4f5f3; --surface:#ffffff; --surface-2:#f0f0ed; --hover:#eaeae7;
  --border:rgba(0,0,0,.10); --border-2:rgba(0,0,0,.20);
  --ink:#141414; --ink-2:#4a4946; --ink-3:#6a6862;   /* see the dark ramp note */
  --accent:#0c8093; --accent-dim:rgba(12,128,147,.13); --accent-contrast:#ffffff;
  /* Darkened from #0e97ad so the WHITE label on a .btn.primary clears 4.5:1;
     it was 3.47:1, i.e. the most important control on every page failed.
     Still 4.1:1+ as a border/fill, well over the 3:1 those need. */
  /* On-tint text. Tuned against the worst parent a tinted pill sits on
     (--hover, not just --surface), which is where they were landing at
     4.1-4.5:1. */
  --good-ink:#127140; --warn-ink:#8a5a00; --crit-ink:#ad3434; --accent-ink:#0a6878;
  --c1:#2a78d6; --c2:#1baf7a; --c3:#eda100; --c4:#008300;
  --c5:#4a3aa7; --c6:#e34948; --c7:#e87ba4; --c8:#eb6834;
  --grid:rgba(0,0,0,.06); --axis:rgba(0,0,0,.16);
  --map-bg:#e8e8e4; --shadow:rgba(0,0,0,.2);
  --topbar-bg:rgba(244,245,243,.82);
  --glow-1:rgba(14,151,173,.08); --glow-2:rgba(14,151,173,.04);
}
@media (prefers-color-scheme: light){
  html:not([data-theme]){
    --bg:#f4f5f3; --surface:#ffffff; --surface-2:#f0f0ed; --hover:#eaeae7;
    --border:rgba(0,0,0,.10); --border-2:rgba(0,0,0,.20);
    --ink:#141414; --ink-2:#4a4946; --ink-3:#6a6862; /* keep in step with the block above */
    --accent:#0c8093; --accent-dim:rgba(12,128,147,.13); --accent-contrast:#ffffff;
  /* Darkened from #0e97ad so the WHITE label on a .btn.primary clears 4.5:1;
     it was 3.47:1, i.e. the most important control on every page failed.
     Still 4.1:1+ as a border/fill, well over the 3:1 those need. */
    --good-ink:#127140; --warn-ink:#8a5a00; --crit-ink:#ad3434; --accent-ink:#0a6878;
    --c1:#2a78d6; --c2:#1baf7a; --c3:#eda100; --c4:#008300;
    --c5:#4a3aa7; --c6:#e34948; --c7:#e87ba4; --c8:#eb6834;
    --grid:rgba(0,0,0,.06); --axis:rgba(0,0,0,.16);
    --map-bg:#e8e8e4; --shadow:rgba(0,0,0,.2);
    --topbar-bg:rgba(244,245,243,.82);
    --glow-1:rgba(14,151,173,.08); --glow-2:rgba(14,151,173,.04);
  }
}

/* ---------- base ---------- */
*{ box-sizing:border-box; }
body{
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--font-sans); line-height:1.5;
  -webkit-font-smoothing:antialiased; letter-spacing:.1px;
}
body.glow{
  background:
    radial-gradient(1100px 560px at 82% -8%, var(--glow-1), transparent 60%),
    radial-gradient(900px 500px at 10% 120%, var(--glow-2), transparent 55%),
    var(--bg);
}
/* --accent is a UI colour (borders, fills, focus rings) where 3:1 is the bar;
   as body text it needs 4.5:1, which is what --accent-ink is for. */
a{ color:var(--accent-ink); text-decoration:none; }
a:hover{ text-decoration:underline; }
:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.mono,.font-monospace{ font-family:var(--font-mono)!important; }
.tnum{ font-variant-numeric:tabular-nums; }
.muted{ color:var(--ink-3); } .sec{ color:var(--ink-2); }

/* ---------- type scale ----------
   h1 (page title) → .sec-title (section) → .ptitle (panel, uppercase)
   .lede (intro) → .body (prose) → .sec-sub (section caption) */
h1{ font-size:clamp(1.8rem,4.5vw,2.6rem); line-height:1.06; letter-spacing:-.9px;
  font-weight:800; margin:0 0 .9rem; }
.eyebrow{ font-size:.74rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase;
  color:var(--accent-ink); margin-bottom:.7rem; }
.lede{ font-size:1.08rem; line-height:1.55; margin:0 0 1rem; max-width:46rem; }
.body{ font-size:.96rem; line-height:1.62; color:var(--ink-2); margin:0 0 1rem; max-width:46rem; }
.body strong, .body b{ color:var(--ink); }
.sec-title{ font-size:1.25rem; font-weight:700; letter-spacing:-.3px; margin:0 0 .3rem;
  display:flex; align-items:center; gap:.5rem; }
.sec-sub{ color:var(--ink-3); font-size:.9rem; margin:0 0 1.2rem; }
.ptitle{ font-size:.72rem; font-weight:600; letter-spacing:.07em; text-transform:uppercase;
  color:var(--ink-2); margin:0; display:flex; align-items:center; gap:.5rem; }
.ptitle .sub{ text-transform:none; letter-spacing:0; font-weight:400; color:var(--ink-3); font-size:.72rem; }
.hero{ padding:2.8rem 0 1.6rem; }

/* ---------- layout ---------- */
.container{ max-width:var(--page-w,960px); margin:0 auto; padding:0 1.4rem; }
.foot{ color:var(--ink-3); font-size:.82rem; padding:1.4rem; border-top:1px solid var(--border);
  display:flex; flex-wrap:wrap; gap:.4rem 1.2rem; justify-content:center; margin-top:2rem; }

/* ---------- topbar ---------- */
.topbar{ display:flex; align-items:center; flex-wrap:wrap; gap:.6rem; padding:.6rem 1rem;
  position:sticky; top:0; z-index:1030; background:var(--topbar-bg);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  border-bottom:1px solid var(--border); }
.brand{ font-weight:800; font-size:1.05rem; letter-spacing:-.3px;
  display:flex; align-items:center; gap:.55rem; color:var(--ink); text-decoration:none; }
.brand:hover{ text-decoration:none; }
.brand .dot{ width:10px; height:10px; border-radius:50%; background:var(--accent);
  box-shadow:0 0 0 4px var(--accent-dim), 0 0 12px 1px var(--accent-dim); }
.top-actions{ margin-left:auto; display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; }
/* tab strip scrolls inside itself on narrow screens instead of widening the page */
.navtabs{ display:flex; gap:.25rem; background:var(--surface-2); border:1px solid var(--border);
  border-radius:9px; padding:.18rem; max-width:100%; overflow-x:auto; scrollbar-width:none; }
.navtabs::-webkit-scrollbar{ display:none; }
.navtab{ font-size:.78rem; font-weight:600; color:var(--ink-2); text-decoration:none;
  padding:.24rem .7rem; border-radius:7px; transition:.15s; white-space:nowrap; }
.navtab:hover{ color:var(--ink); text-decoration:none; }
.navtab.active{ color:var(--ink); background:var(--surface); box-shadow:0 1px 4px var(--shadow);
  border:1px solid var(--border); margin:-1px; }

/* ---------- controls ---------- */
.btn{ display:inline-flex; align-items:center; gap:.4rem; font-weight:600; font-size:.9rem;
  padding:.55rem 1rem; border-radius:10px; border:1px solid var(--border-2);
  color:var(--ink); background:var(--surface-2); transition:.15s; cursor:pointer; }
.btn:hover{ border-color:var(--accent); background:var(--surface); text-decoration:none; }
.btn.primary{ background:var(--accent); border-color:var(--accent); color:var(--accent-contrast); }
.btn.primary:hover{ filter:brightness(1.08); background:var(--accent); }
.btn.sm{ padding:.4rem .8rem; font-size:.84rem; }
.iconbtn{ background:var(--surface-2); border:1px solid var(--border); color:var(--ink-2);
  border-radius:8px; width:32px; height:32px; display:grid; place-items:center;
  cursor:pointer; font-size:.9rem; transition:.15s; }
.iconbtn:hover{ color:var(--ink); border-color:var(--accent); background:var(--hover); }
.ctl{ background:var(--surface-2); border:1px solid var(--border); color:var(--ink);
  border-radius:8px; font-size:.8rem; padding:.32rem .6rem; }
.ctl:focus{ outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-dim); }

/* ---------- surfaces ---------- */
.panel{ background:var(--surface); border:1px solid var(--border); border-radius:14px;
  box-shadow:0 1px 0 rgba(255,255,255,.02) inset, 0 8px 24px -18px var(--shadow); }
.panel-b{ padding:1rem 1.1rem; }
/* prose panels (marketing/info pages) get roomier padding */
.panel.pad{ padding:1.6rem 1.5rem; margin-bottom:1.4rem; border-radius:16px; }

/* ---------- badges / pills / chips ---------- */
.badge{ font-size:.66rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  padding:.15rem .5rem; border-radius:999px; border:1px solid var(--border); }
.badge.on{ color:var(--good-ink); background:rgba(46,194,110,.12); border-color:rgba(46,194,110,.35); }
.badge.want{ color:var(--warn-ink); background:rgba(250,178,25,.12); border-color:rgba(250,178,25,.35); }
.badge.partner{ color:var(--accent-ink); background:var(--accent-dim); border-color:var(--accent); }
.badge.off{ color:var(--ink-3); }
.pill{ font-size:.66rem; font-weight:600; padding:.12rem .48rem; border-radius:999px;
  background:var(--surface-2); color:var(--ink-2); border:1px solid var(--border); }
.chip{ font-size:.78rem; color:var(--ink-2); background:var(--surface-2);
  border:1px solid var(--border); border-radius:999px; padding:.05rem .5rem; }
.status{ display:inline-flex; align-items:center; gap:.4rem; font-size:.7rem; font-weight:600;
  padding:.14rem .5rem; border-radius:999px; vertical-align:middle; }
.status .sd{ width:7px; height:7px; border-radius:50%; }
.status.on{ color:var(--good-ink); background:rgba(46,194,110,.12); }
.status.on .sd{ background:var(--good); box-shadow:0 0 8px var(--good); }
.status.off{ color:var(--crit-ink); background:rgba(225,91,91,.12); }
.status.off .sd{ background:var(--crit); }

/* ---------- steps (guides) ---------- */
.steps{ display:grid; gap:.9rem; margin:0; }
.step{ display:flex; gap:.85rem; align-items:flex-start; }
.step .num{ flex:none; width:26px; height:26px; border-radius:50%; display:grid; place-items:center;
  font-size:.8rem; font-weight:700; color:var(--accent-ink); background:var(--accent-dim);
  border:1px solid var(--accent); margin-top:.1rem; }
.step .stxt{ color:var(--ink-2); font-size:.95rem; line-height:1.55; }
.step .stxt b{ color:var(--ink); }

/* ---------- data tables (app pages) ---------- */
.tbl{ width:100%; font-size:.82rem; border-collapse:separate; border-spacing:0; }
.tbl thead th{ position:sticky; top:0; background:var(--surface); z-index:1;
  font-size:.66rem; text-transform:uppercase; letter-spacing:.06em; font-weight:600;
  color:var(--ink-3); text-align:left; padding:.5rem .6rem;
  border-bottom:1px solid var(--border); white-space:nowrap; }
.tbl tbody td{ padding:.48rem .6rem; border-bottom:1px solid var(--grid); vertical-align:middle; }
.tbl tbody tr:last-child td{ border-bottom:0; }
.tbl tbody tr{ transition:background .12s; }
.tbl tbody tr:hover{ background:var(--hover); }
.scroll{ overflow:auto; }
.scroll::-webkit-scrollbar{ width:10px; height:10px; }
.scroll::-webkit-scrollbar-thumb{ background:var(--surface-2); border-radius:99px;
  border:2px solid var(--surface); }

/* ---------- KPI tiles (app pages) ---------- */
.kpi{ background:var(--surface); border:1px solid var(--border); border-radius:14px;
  padding:.85rem 1rem; position:relative; overflow:hidden; }
.kpi::before{ content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--accent); opacity:.8; }
.kpi .k-label{ font-size:.68rem; text-transform:uppercase; letter-spacing:.08em;
  color:var(--ink-3); font-weight:600; }
.kpi .k-val{ font-size:1.5rem; font-weight:700; line-height:1.1; margin-top:.15rem;
  letter-spacing:-.5px; }
.kpi .k-val small{ font-size:.8rem; font-weight:500; color:var(--ink-3); letter-spacing:0; }

/* ---------- toast ---------- */
.toast-msg{ position:fixed; bottom:22px; left:50%; transform:translateX(-50%) translateY(6px);
  background:var(--surface-2); border:1px solid var(--border-2); color:var(--ink);
  padding:.5rem .95rem; border-radius:11px; font-size:.82rem; z-index:2000;
  box-shadow:0 12px 32px -10px var(--shadow); opacity:0; pointer-events:none; transition:.22s; }
.toast-msg.show{ opacity:1; transform:translateX(-50%) translateY(0); }

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

/* ---------- topbar on a phone ----------
   The app pages (index/node/observer) wrap the whole bar in ONE non-wrapping
   `.container-fluid.d-flex`, so at 390px every child competed for the same
   line. `.navtabs` lost every time: it sets overflow-x, which zeroes its
   automatic min-width, so it shrank to a ~8px sliver while the filter selects
   kept their intrinsic width — and the account controls were pushed past the
   right edge with no way to scroll to them. Signing in made it worse by adding
   three tabs and the user pill.

   Give the bar three stacked rows instead: brand + controls, then the tab
   strip full-width (scrolling inside itself, as it already knows how to), then
   the filters sharing a row. `.topbar` itself already wraps, so the static
   pages (alerts/radios/new-to-mesh) never had this and are untouched. */
@media (max-width: 767.98px){
  .topbar{ padding:.5rem .75rem; }
  .topbar > .container-fluid{ flex-wrap:wrap; gap:.45rem .5rem!important; }
  /* `display:contents` flattens the right-hand action group so its children
     become flex items of the bar itself. Without it they stay trapped on one
     line together and the bar needs a fourth row. The `!important` is not
     optional: the group carries Bootstrap's `.d-flex`, which is itself
     `display:flex !important` and wins otherwise. */
  .topbar > .container-fluid > .ms-auto{ display:contents!important; }
  /* `display:contents` moves boxes, not DOM position — the flattened children
     are still selector-children of the group, so these stay `.ms-auto > `.
     Scoping to that also keeps the logout button (nested in .userbox) out of
     the `:first-of-type` match below. Everything ahead of the group — brand,
     the `/` separator, the node name — keeps order 0 and its DOM sequence;
     ordering `.brand` instead would reshuffle node.html, which labels its
     subject with a second `.brand`. */
  .topbar .ms-auto > .iconbtn{ order:2; }
  .topbar .ms-auto > .iconbtn:first-of-type{ margin-left:auto; }  /* ms-auto's box is gone */
  .topbar .ms-auto > .userbox{ order:3; }
  /* A zero-height full-width flex item — the container's own ::before — forces
     the filters onto their own row. No flex-basis can do this reliably: row 1
     has ~200px spare when signed out and ~90px when signed in, so any basis
     that clears the signed-in case still half-fits one select in the signed-out
     one. `:has()` keeps the empty row off node.html, which has no filters; a
     browser without `:has()` just drops the rule and gets the old crowding. */
  .topbar > .container-fluid:has(.ms-auto > .ctl)::before{
    content:""; order:4; flex:1 0 100%; height:0; }
  .topbar .ms-auto > .ctl{ order:5; flex:1 1 45%; min-width:0; }
  .topbar .navtabs{ order:6; flex:1 0 100%; }
  .topbar .updated{ order:7; flex:1 0 100%; }
}
