/* ============================================================
   YMRTECH — PROBE THEME  (the single source of truth)
   ============================================================
   The visual language of https://ymrtech.com/account/speed-test,
   generalised into one flat stylesheet that every surface uses:
   the public site, the client portal (/account, /install, /account/*)
   and the operator console (/admin).

   RULES FOR EVERYONE WHO TOUCHES A PAGE:

     1. Never write a colour literal in a page. Use var(--…). The only
        hex values in the whole estate live in the :root block below.
     2. Spacing/radius/type scale come from the tokens too (--r, --gap,
        --wrap). If a page needs a new value, add it HERE, not inline.
     3. A page may add layout rules (grids, widths, page-specific bits)
        in its own <style>, but the palette, the card/pill/table/terminal
        components and the header/footer chrome come from this file.
     4. This file carries the chrome's rules AND the icon font, so the
        portal renders the same header, footer and glyphs from it.
     5. Keep a copy of this file in the clientsv repo byte-identical to
        the one in the ymrtech site repo (internal/api/static/theme.css
        vs assets/css/theme.css) — it is duplicated on purpose: the
        portal must render with the marketing site down.

   The palette is the tunnel-probe palette: near-black ground, one
   signal green, one cyan for links/accents, amber for warnings, red for
   errors, and a desaturated teal-grey for body text. The whole page is
   monospace; headings are small, uppercase and letter-spaced so the
   content — not the chrome — has the weight.
   ============================================================ */

/* --- Icons -----------------------------------------------------------------
   There is no icon font, and no @font-face. Every icon is an inline svg on a
   16x16 grid, drawn by _includes/icon.html on site pages and by the shared
   chrome in internal/api/chromeicons.go — which is GENERATED from the site's
   registry (_data/icons.yml), so the two halves cannot disagree about a
   drawing. tools/icon-svg.py is that generator and the offline renderer.

   This replaced a self-hosted Nerd Font SUBSET: private-use code points the
   page asked the browser for. It was a build artifact with its own regeneration
   gate, a network request, and a failure mode that no diff could show — a code
   point the subset did not map rendered as a tofu box for the reader. An inline
   svg is markup: it cannot go missing, it adds no request, and it inherits the
   wrapper's colour token.

   The drawings are stroke-only (fill:none, stroke:currentColor, 1.5 wide, round
   caps) and the colour is NEVER set in the icon — the token on the wrapper
   decides it. See the icons section further down for the sizing rules. */

/* --- Font stack ----------------------------------------------------------
   The stack lives in this file's :root and is served to ymrtech.com and to the
   client portal alike — the same file in both places. Nothing is set here. */

:root {
  /* ── surface ─────────────────────────────────────────────── */
  --bg:        #04070b;   /* page ground */
  --bg-glow:   #0c2230;   /* the radial wash behind the header */
  --panel:     #0a1017;   /* cards, terminal blocks */
  --panel2:    #0d151d;   /* inputs, inner tiles, table stripes */
  --line:      #16333c;   /* every border, every rule */
  --line-hot:  #1d4a3a;   /* border on hover / of a live element */

  /* ── signal ──────────────────────────────────────────────── */
  --green:     #2bff88;   /* the one bright colour: values, ok, brand */
  --green-dim: #127a48;   /* green at rest: rails, borders, dots — never ink */
  --cyan:      #35e6ff;   /* section headings, live readouts, info */
  --amber:     #ffb454;   /* warnings, peak, pending */
  --red:       #ff5f6d;   /* errors, revoked, failures */
  --link:      #e06c9f;   /* content links — a rose, so a link never reads as a
                             heading (cyan) or an error (red) */

  /* ── text ────────────────────────────────────────────────── */
  --text:      #cfe9e4;   /* body */
  --muted:     #86a4b1;   /* secondary, labels, meta */
  --dim:       #4fbd85;   /* de-emphasised TEXT: prompt output, ascii art */

  /* ── derived signal fills (kept here, never inline) ──────── */
  --green-fill: #08170f;  /* primary button ground */
  --green-hi:   #8affc4;  /* the lighter green the wire canvas paints its live glyphs */
  --amber-fill: #1c1408;  /* warn note ground */
  --amber-line: #4a3a12;  /* warn note border */
  --red-fill:   #1d0b0e;  /* error note ground */
  --cyan-fill:  #07171d;  /* info note ground */
  --hover:      rgba(43, 255, 136, .06);
  --active:     rgba(43, 255, 136, .14);  /* the current row in a dropdown */
  --overlay:    rgba(0, 0, 0, .75);  /* modal scrim (the QR sheet) */
  --on-light:   #ffffff;  /* the light ground a QR code needs to be scannable */
  --scan:       rgba(0, 0, 0, .22);    /* the CRT veil */
  --glow-c:     rgba(43, 255, 136, .25);

  /* ── geometry & type ─────────────────────────────────────── */
  /* One stack for the whole estate, on both surfaces this file serves. It is
     all latin: the icon font used to lead it, and with every glyph replaced by
     an inline svg there is nothing left for a private-use face to draw — so the
     stack starts at the real typeface. --font-mono is kept as an alias for
     anything that still asks by that name. */
  --mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace;
  --wrap: 940px;          /* the reading column, everywhere */
  --r: 8px;               /* card/panel radius */
  --r-sm: 6px;            /* control radius */
  --gap: 1rem;

  /* ── type scale ───────────────────────────────────────────────
     One dial for every piece of type in the estate. This palette is
     monospace at a 14px base, which on a phone reads as a wall of 11-14px:
     the labels, the meta lines and the terminal panes were all set at the
     size of a footnote. --scale multiplies every size in this file and in
     each surface's own stylesheet, so the whole estate steps up together
     and the hierarchy (title > body > label) is kept exactly. Geometry
     (--gap, paddings, --wrap) is deliberately NOT scaled: nothing reflows,
     only the letters grow. */
  --scale: 1.22;                          /* the whole scale below rides on this */
  --fs:    calc(14px   * var(--scale));   /* body   (was 14px) */
  --fs-sm: calc(.82rem * var(--scale));   /* small  (was .82rem) */
  --fs-xs: calc(.72rem * var(--scale));   /* labels (was .72rem) */
  --glow: 0 0 6px var(--glow-c);
}

/* ── light ground ─────────────────────────────────────────────────
   The probe look on white paper: the same skeleton, the same monospace,
   the same small uppercase letter-spaced headings — only the ground and
   the ink invert, and every signal colour is darkened so it holds its
   contrast on white instead of glowing. --on-light stays #ffffff in both
   schemes: a QR code needs a white ground to be scannable.

   Mechanism: two explicit token sets, selected by (a) the OS preference
   via @media (prefers-color-scheme: light), and (b) the data-theme the
   switch pins. Deliberately not a feature-query-guarded colour function:
   that is unavailable on phones and tablets a couple of years old, and an
   engine that skips it keeps the dark palette for everyone who asked for
   light, on every page, however often the switch is tapped. Plain media
   queries and attribute selectors work everywhere, scripting or not.
   The two light blocks below are identical — the theme gate compares
   them, and fails if they ever drift apart. */

/* 1. the OS asked for light and nothing is pinned */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg:         #f7f9fb;
    --bg-glow:    #e3edf4;
    --panel:      #ffffff;
    --panel2:     #f1f5f8;
    --line:       #d6e0e6;
    --line-hot:   #8fc4aa;
    --green:      #0b7a45;
    --green-dim:  #7dad95;
    --green-hi:   #14955a;
    --cyan:       #0a7391;
    --amber:      #8f5b00;
    --red:        #bf2a39;
    --link:       #b03a72;
    --text:       #26343a;
    --muted:      #4e5f67;
    --dim:        #2d6a51;
    --green-fill: #e7f5ed;
    --amber-fill: #fdf4e2;
    --amber-line: #e9d5a6;
    --red-fill:   #fdecee;
    --cyan-fill:  #e7f4f9;
    --hover:      rgba(11, 122, 69, .07);
    --active:     rgba(11, 122, 69, .14);  /* the current row in a dropdown */
    --overlay:    rgba(24, 44, 54, .55);
    --scan:       rgba(24, 44, 54, .05);
    --glow-c:     rgba(11, 122, 69, .18);
  }
}

/* 2. an explicit choice: light. (The dark pin needs no token block — the
   :root set above is already dark; it only re-declares color-scheme.) */
:root[data-theme="light"] {
  color-scheme: light;
  --bg:         #f7f9fb;
  --bg-glow:    #e3edf4;
  --panel:      #ffffff;
  --panel2:     #f1f5f8;
  --line:       #d6e0e6;
  --line-hot:   #8fc4aa;
  --green:      #0b7a45;
  --green-dim:  #7dad95;
  --green-hi:   #14955a;
  --cyan:       #0a7391;
  --amber:      #8f5b00;
  --red:        #bf2a39;
  --link:       #b03a72;
  --text:       #26343a;
  --muted:      #4e5f67;
  --dim:        #2d6a51;
  --green-fill: #e7f5ed;
  --amber-fill: #fdf4e2;
  --amber-line: #e9d5a6;
  --red-fill:   #fdecee;
  --cyan-fill:  #e7f4f9;
  --hover:      rgba(11, 122, 69, .07);
  --active:     rgba(11, 122, 69, .14);  /* the current row in a dropdown */
  --overlay:    rgba(24, 44, 54, .55);
  --scan:       rgba(24, 44, 54, .05);
  --glow-c:     rgba(11, 122, 69, .18);
}

/* ── base ────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html { color-scheme: light dark; -webkit-text-size-adjust: 100%; }

/* An explicit choice beats the OS preference. data-theme is written before
   first paint by the inline one-liner in each page's <head> (no flash of
   the wrong theme) and thereafter by the .theme-toggle switch; the choice
   lives in localStorage under "ymr-theme", and its absence means "follow
   the OS", which is also the default for a first-time visitor. */
:root[data-theme="dark"]  { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }

body {
  margin: 0;
  background: var(--bg);
  background-image: radial-gradient(1200px 500px at 50% -180px, var(--bg-glow) 0%, transparent 70%);
  background-repeat: no-repeat;
  color: var(--text);
  font: var(--fs)/1.55 var(--mono);
  overflow-x: hidden;
}

/* Links carry the rose at rest, so a link is never mistaken for a heading:
   the h1 is green, h2/h3 and card titles are cyan, and the only red on the
   page means an error. The underline is deliberately NOT set here — it is a
   prose affordance, scoped to running text in style.css. Set in this shared
   file it would also underline the chrome (header, footer, buttons) on the
   clientsv portal and console that consume this same stylesheet, which is
   exactly the surface where an airy restyle is the wrong call. */
a { color: var(--link); text-decoration: none; }

/* the fixed CRT scanline veil — one div, .scan, first child of body.
   Multiply blending keeps it a texture, not a layer of grey. */
.scan {
  position: fixed; inset: 0; pointer-events: none; z-index: 9;
  background: repeating-linear-gradient(180deg, var(--scan) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}
@media (prefers-reduced-motion: reduce) { .scan { display: none; } }

/* ── layout ──────────────────────────────────────────────────── */
.wrap { max-width: var(--wrap); margin: 0 auto; padding: 1.4rem 1rem 4rem; }
.container { max-width: 1100px; margin: 0 auto; padding: 0 1rem; }
.section { padding: 2rem 0; }
.stack { display: grid; gap: var(--gap); }
.row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.sep { color: var(--muted); }

/* ── headings: small, uppercase, letter-spaced ───────────────── */
h1, h2, h3, h4 {
  margin: 0 0 .6rem;
  font-weight: 400;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1.3;
}
h1 { font-size: calc(1.05rem * var(--scale)); color: var(--green); }
h2 { font-size: calc(.78rem * var(--scale)); color: var(--cyan); margin-bottom: .7rem; }
h3 { font-size: calc(.78rem * var(--scale)); color: var(--cyan); letter-spacing: .14em; }
h4 { font-size: var(--fs-sm); color: var(--muted); letter-spacing: .12em; }
h1 .cur {                      /* the blinking caret after a page title */
  display: inline-block; width: .6ch; background: var(--green);
  animation: b 1.05s steps(1) infinite;
}
@keyframes b { 50% { opacity: 0 } }
@media (prefers-reduced-motion: reduce) { h1 .cur { animation: none } }

.sub { color: var(--muted); font-size: var(--fs-sm); margin: .35rem 0 1.1rem; }

p { margin: .6rem 0; }
ul, ol { padding-left: 1.3rem; }
li { margin: .25rem 0; }
hr { border: 0; border-top: 1px solid var(--line); margin: 1.4rem 0; }

/* muted / dim / comment text */
.muted, .dim { color: var(--muted); }
.dim { color: var(--dim); }
.comment { color: var(--muted); font-style: italic; }
.accent { color: var(--cyan); }
.text-secondary { color: var(--muted); }
.small { font-size: var(--fs-sm); }
.color-success { color: var(--green); }
.color-info    { color: var(--cyan); }
.color-warning { color: var(--amber); }
.color-error   { color: var(--red); }
.color-git     { color: var(--muted); }

code, kbd, samp {
  font: inherit; color: var(--green); background: var(--panel2);
  border: 1px solid var(--line); border-radius: 4px; padding: 0 .3rem;
}
pre {
  font: calc(12.5px * var(--scale))/1.6 var(--mono); color: var(--text); background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r); padding: .8rem 1rem;
  overflow-x: auto; margin: .8rem 0;
}
pre code { border: 0; background: none; padding: 0; color: inherit; }

blockquote {
  margin: .9rem 0; padding: .1rem 1rem; color: var(--muted);
  border-left: 2px solid var(--line);
}

/* ── cards ───────────────────────────────────────────────────── */
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1rem; margin: 0 0 1rem;
}
.card > h2, .card > .card-title { font-size: calc(.78rem * var(--scale)); letter-spacing: .14em; text-transform: uppercase; color: var(--cyan); margin: 0 0 .7rem; }
.card-header {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  border-bottom: 1px solid var(--line); padding-bottom: .5rem; margin: -.25rem 0 .8rem;
}
.card-body > :first-child { margin-top: 0 }
.card-body > :last-child { margin-bottom: 0 }
.card-text { color: var(--text); font-size: var(--fs-sm); }
.card-actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .7rem; }
.card.warnbox, .warnbox { border-color: var(--amber-line); }
.card.badbox, .badbox { border-color: var(--red); }

/* ── controls ────────────────────────────────────────────────── */
button, .btn, input[type="submit"] {
  font: inherit; background: transparent; color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: .5rem .9rem; cursor: pointer; display: inline-block;
}
button:hover:not(:disabled), .btn:hover { border-color: var(--green); color: var(--green); text-decoration: none; }
button.primary, .btn.primary {
  border-color: var(--green-dim); color: var(--green); background: var(--green-fill);
}
.btn-cyan { border-color: var(--line); color: var(--cyan); }
.btn-cyan:hover { border-color: var(--cyan); color: var(--cyan); }
button:disabled { opacity: .4; cursor: not-allowed; }

/* ── the theme switch ─────────────────────────────────────────────
   One control on every surface: the public site's header, the portal
   topbar, the console bar. A pen-drawn half-disc rather than an icon
   glyph — the same trick as the hamburger, so the Nerd font subset never
   needs regenerating — the live scheme spelled out, and a 44px hit area
   that works with a thumb. It is never inside the collapsing mobile menu:
   it stays in the bar, next to the hamburger.

   The label names the scheme you are IN, not the one a tap would give
   you, and aria-pressed is true when light is on. Shift-click (desktop)
   or long-press (mobile) clears the pinned choice and hands the decision
   back to the OS — see theme.js, which is the only code that writes
   data-theme. */
.theme-toggle {
  display: inline-flex; align-items: center; gap: .45rem;
  min-height: 44px; padding: .4rem .65rem;
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--muted);
  font: inherit; font-size: var(--fs-xs); line-height: 1;
  text-transform: uppercase; letter-spacing: .12em;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.theme-toggle:hover, .theme-toggle:active { border-color: var(--green-dim); color: var(--text); }
.theme-toggle:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.theme-toggle .ico {
  width: 14px; height: 14px; flex: none; border-radius: 50%;
  border: 1.5px solid currentColor;
  background: linear-gradient(90deg, currentColor 0 50%, transparent 50%);
}
.theme-toggle[aria-pressed="true"] .ico {
  background: linear-gradient(90deg, transparent 0 50%, currentColor 50%);
}
/* the same switch folded into a status line or a footer: no chrome, it
   inherits the colour and the type around it */
.theme-toggle.bare {
  min-height: 0; padding: 0; background: none; border: 0;
  color: inherit; letter-spacing: inherit; font-size: inherit;
  text-decoration: underline; text-underline-offset: 2px; text-decoration-style: dotted;
}
.theme-toggle.bare .ico { width: 11px; height: 11px; border-width: 1px; }
/* a phone keeps the switch, drops the word — the header one only: the bare
   variant folded into a status line has no room to shrink into a lone disc */
@media (max-width: 520px) {
  .theme-toggle:not(.bare) .txt { display: none; }
  .theme-toggle:not(.bare) { min-width: 44px; justify-content: center; }
  .theme-toggle:not(.bare) .ico { width: 17px; height: 17px; }
}

/* --- The language switch -------------------------------------------------
   Beside the theme switch, in the bar, and the same shape as the nav's group
   menus: a label over a checkbox with the menu as its sibling, so it opens with
   CSS alone and works with scripting off — which is the whole reason it is not
   a scripted dropdown.

   This block is the portal's own (internal/api/static/theme.css), carried here
   because the bar the site serves IS the portal's bar: _layouts/default.html
   renders _includes/chrome/anonymous-header.html, and assets/js/main.js
   re-fetches that same bar from /account/chrome/header at runtime. Without
   these rules the switcher's markup paints as a bare list — the label above
   four endonym rows — because this stylesheet is what styles the shared chrome.

   The bar is a flex row whose layout is set by `order`. The switch deliberately
   takes the theme switch's OWN order (5 here, 3 once the bar collapses): equal
   order means DOM order decides, and the switch is written immediately before
   the theme switch, so it lands on its inner side at every width without moving
   the theme switch or the console's row (order 6, the second line).

   It is in the bar and not in the collapsed panel because the choice has to be
   reachable on a phone in one tap, exactly like the theme switch beside it. */
.lang-switch {
  order: 5; position: relative;
  display: inline-flex; align-items: center; margin-left: 0.5rem;
}
/* The input is the control's state and has to stay focusable, so it is hidden
   as a box rather than removed: the label below is its visible face, and the
   `~` selectors are what open the menu. */
.lang-toggle {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}
.lang-toggle-label {
  display: inline-flex; align-items: center; gap: .4rem;
  min-height: 0; padding: .3rem .55rem;
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--muted);
  font: inherit; font-size: var(--fs-xs); line-height: 1;
  text-transform: uppercase; letter-spacing: .12em;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.lang-toggle-label:hover, .lang-toggle-label:active { border-color: var(--green-dim); color: var(--text); }
.lang-toggle:focus-visible ~ .lang-toggle-label { outline: 2px solid var(--cyan); outline-offset: 2px; }
.lang-toggle-label .ico { display: inline-flex; align-items: center; width: 16px; height: 16px; flex: none; }
.lang-toggle-label .ico > svg { display: block; width: 16px; height: 16px; }
.lang-menu {
  display: none; position: absolute; top: 100%; right: 0; z-index: 20;
  margin: .35rem 0 0; padding: .25rem; min-width: 12rem;
  list-style: none; text-align: left;
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--r-sm);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .35);
}
.lang-toggle:checked ~ .lang-menu { display: block; }
.lang-menu li { margin: 0; }
.lang-menu a {
  display: flex; align-items: center; gap: .45rem;
  padding: .35rem .5rem; border-radius: var(--r-sm);
  color: var(--muted); font-size: var(--fs-xs);
  text-decoration: none; white-space: nowrap;
}
.lang-menu a:hover, .lang-menu a:focus-visible { color: var(--text); background: var(--panel); text-decoration: none; }
.lang-menu a[aria-current="true"] { color: var(--green); }
.lang-menu a .i { width: 16px; height: 16px; flex: none; }
.lang-menu .endonym { letter-spacing: normal; }
.lang-menu .code { margin-left: auto; opacity: .8; letter-spacing: .1em; }

@media (max-width: 1200px) {
  .lang-switch { order: 3; margin-left: .6rem; }
}
@media (max-width: 520px) {
  /* A phone keeps the flag, which is the code's own symbol; the panel is then
     anchored to the left edge so it cannot open off-screen. */
  .lang-switch .lang-toggle-label .txt { display: none; }
  .lang-switch .lang-toggle-label { min-width: 44px; justify-content: center; }
  .lang-menu { right: auto; left: 0; }
}

select, input, textarea, .form-control, .form-select {
  font: inherit; background: var(--panel2); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: .45rem .6rem;
}
input:focus, select:focus, textarea:focus, .form-control:focus {
  outline: none; border-color: var(--green-dim);
}
.form-group { margin: .55rem 0 }
.form-label { display: block; color: var(--muted); font-size: var(--fs-xs); letter-spacing: .12em; text-transform: uppercase; margin-bottom: .25rem; }
.form-check-input { accent-color: var(--green); }

/* ── icons ───────────────────────────────────────────────────────
   One family, drawn not typeset: a 16x16 inline svg from the registry in
   _data/icons.yml, rendered by _includes/icon.html on the site and by the
   generated chromeicons.go in the shared bar. The Nerd Font subset is gone —
   the last glyph it drew was the shell prompt, and that is the brand mark now —
   so a new icon adds no build artifact and no code point that could render as a
   tofu box.

   The colour is never set here. The svg strokes with currentColor, so the
   wrapper's token decides it (.color-success, .color-info, a .pill class) and
   an icon needs no rule of its own to sit inside one.

   Sizes come from the svg's width/height attributes, which is why these rules
   set no width: a `size` argument is the truth, and a CSS width would silently
   override every one of them. The rules only place the icon on the baseline and
   stop a flex row from squashing it. */
.i { display: inline-block; vertical-align: -0.125em; flex: none; }
/* an icon standing in for a display figure scales with that figure */
.stat-value .i, .big .v .i { width: .92em; height: .92em; vertical-align: -.06em; }
/* a line in a terminal pane: the icon is a cell of the monospace grid */
.terminal-line .i, .terminal-title .i, .prompt-line .i { width: 1em; height: 1em; vertical-align: -.14em; }
/* an unknown icon name is a build failure; in a rendered page it must not be
   invisible either, so it draws a red cross rather than an empty box */
.i-unknown { color: var(--red) }

/* ── pills / badges / tags ───────────────────────────────────── */
.pill {
  display: inline-block; padding: .05rem .45rem; border: 1px solid var(--line);
  border-radius: 999px; font-size: var(--fs-xs); color: var(--muted);
  letter-spacing: .04em; white-space: nowrap;
}
.pill.on, .pill.ok, .pill.active { border-color: var(--green-dim); color: var(--green); }
.pill.off, .pill.warn, .pill.busy, .pill.retry { border-color: var(--amber-line); color: var(--amber); }
.pill.failed, .pill.err, .pill.revoked { border-color: var(--line); color: var(--red); }
.pill.pipe { color: var(--cyan) }
.pill.privacy, .pill.tier-privacy { color: var(--green); border-color: var(--green-dim); }
.pill.security, .pill.tier-security { color: var(--cyan); border-color: var(--line); }
.pill.tier, .pill.none { color: var(--muted) }
.tag { display: inline-block; font-size: var(--fs-xs); color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: .05rem .5rem; margin-right: .3rem; }
.module-badge { display: inline-block; font-size: var(--fs-xs); padding: .1rem .5rem; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); }
.module-badge.success { color: var(--green); border-color: var(--green-dim); }

/* ── tables ──────────────────────────────────────────────────── */
.tblwrap { overflow-x: auto }
table { width: 100%; border-collapse: collapse; font-size: calc(12.5px * var(--scale)); margin: .6rem 0; }
th, td { text-align: left; padding: .42rem .5rem; border-bottom: 1px solid var(--line); vertical-align: top; }
th {
  color: var(--muted); font-weight: 400; font-size: var(--fs-xs);
  letter-spacing: .12em; text-transform: uppercase; white-space: nowrap;
}
td { white-space: nowrap }
td.wrap, th.wrap { white-space: normal }
tbody tr:hover { background: var(--hover) }

/* ── key/value grids (portal + console) ──────────────────────── */
.kv { display: grid; grid-template-columns: 9rem 1fr; gap: .15rem .75rem; font-size: calc(.92rem * var(--scale)); margin: .5rem 0; }
.kv span:nth-child(odd), .kv dt { color: var(--muted) }
.kv dd { margin: 0 }

/* the readout tiles: label / big value / unit (the probe's "result" row) */
.big { display: grid; grid-template-columns: repeat(3, 1fr); gap: .7rem; }
.big > div { border: 1px solid var(--line); border-radius: var(--r); padding: .7rem; background: var(--panel2); }
.big .k, .stat-label { font-size: var(--fs-xs); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.big .v, .stat-value { font-size: calc(1.55rem * var(--scale)); color: var(--green); margin-top: .25rem; }
.big .u { font-size: var(--fs-xs); color: var(--muted); }
.stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .7rem; margin: 1rem 0; }
.stat-box { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); padding: .8rem; text-align: center; }
.stat-box .stat-value { display: block; font-size: calc(1.3rem * var(--scale)); line-height: 1.2; }
.stat-box .stat-label { display: block; margin-top: .25rem; }
/* A stat box must never push the page wider than the phone it is on. Its
   track floor is the label's longest unbreakable run ("Electromechanical" is
   17 characters at --fs-xs plus .14em of tracking, ~176px), and once --scale
   lifts the type that floor stops fitting two-up on a handset. Below 520px
   the row stacks, the same move .card-grid already makes; the wrap is the
   last-resort guard so a label breaks rather than clipping under body's
   overflow-x: hidden. */
.stat-box { min-width: 0; }
.stat-box .stat-label { overflow-wrap: anywhere; }
@media (max-width: 520px) {
  .stats-row { grid-template-columns: 1fr; }
}
.verdict { margin-top: .8rem; font-size: var(--fs-sm); color: var(--cyan) }
.verdict.warn, .verdict.unk { color: var(--amber) }
.err, .error { color: var(--red); font-size: var(--fs-sm); min-height: 1.2em; margin: .25rem 0 }
.note {
  border: 1px solid var(--line); border-left: 2px solid var(--cyan);
  border-radius: var(--r-sm); background: var(--panel2);
  padding: .55rem .8rem; margin: .7rem 0; font-size: var(--fs-sm); color: var(--muted);
}
.note.warn { border-left-color: var(--amber); background: var(--amber-fill); border-color: var(--amber-line) }
.note.err  { border-left-color: var(--red);   background: var(--red-fill); border-color: var(--red) }
/* a verdict as a boxed panel — the DNS leak test's result, distinct from the
   one-line .verdict that sits under a result card */
.verdictbox { border: 1px solid var(--line); border-radius: var(--r); padding: 1rem 1.1rem; margin: 1rem 0; background: var(--panel) }
.verdictbox h2 { margin: 0 0 .35rem }
.verdictbox.ok  { border-color: var(--green-dim); background: var(--green-fill) }
.verdictbox.bad { border-color: var(--amber-line); background: var(--amber-fill) }
.verdictbox.unk { border-color: var(--line); background: var(--panel) }
.pill.parked { font-size: var(--fs-xs); color: var(--muted) }
.err.ok { color: var(--green) }
.livebox { border-top: 1px dashed var(--line); margin-top: .6rem; padding-top: .4rem }
.rulelist { list-style: none; padding: 0; margin: .5rem 0 }
.rulelist li { border-bottom: 1px solid var(--line); padding: .35rem 0; font-size: calc(12.5px * var(--scale)) }
.hidden { display: none !important }

/* ── terminal blocks (log panes, command lists) ──────────────── */
.terminal, .terminal-window, .term {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; margin: .9rem 0;
}
.terminal-header {
  display: flex; align-items: center; gap: .4rem; padding: .4rem .7rem;
  border-bottom: 1px solid var(--line); background: var(--panel2);
}
.terminal-title { font-size: var(--fs-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-left: .3rem }
.terminal-dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--line) }
.terminal-dot.green  { background: var(--green-dim) }
.terminal-dot.yellow { background: var(--amber) }
.terminal-dot.red    { background: var(--red) }
.terminal-body, .tui {
  padding: .7rem .9rem; white-space: pre; overflow-x: auto; margin: 0;
  font: calc(12.5px * var(--scale))/1.6 var(--mono); color: var(--green);
}
.terminal-body .dim, .tui .dim { color: var(--dim) }
.terminal-body .vel, .tui .vel { color: var(--cyan) }
.terminal-body .peak, .tui .peak { color: var(--amber) }
.terminal-body .err, .terminal-body .errline { color: var(--red) }
.terminal-line { font-size: calc(12.8px * var(--scale)); line-height: 1.7; color: var(--text); white-space: pre-wrap }
.terminal-line .ok { color: var(--green) }
.terminal-line .warn { color: var(--amber) }
.terminal-line .err { color: var(--red) }
.terminal-line .t { color: var(--muted) }
.log { white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; font-size: calc(12.5px * var(--scale)) }
.log .t { color: var(--muted) } .log .ok { color: var(--green) }
.log .warn { color: var(--amber) } .log .err { color: var(--red) }

/* shell prompt lines (site hero + guides) */
.prompt-line { font-size: calc(12.8px * var(--scale)); line-height: 1.8; white-space: pre-wrap }
.prompt { color: var(--green) }
.cmd { color: var(--text) }
.output { color: var(--muted) }
.output.accent { color: var(--cyan) }

/* ── tiers (portal chooser) ──────────────────────────────────── */
.tiercards { display: grid; gap: .5rem; margin: .6rem 0 .25rem }
.tiercard {
  display: flex; gap: .6rem; align-items: flex-start; border: 1px solid var(--line);
  border-radius: var(--r); padding: .55rem .75rem; cursor: pointer;
  background: var(--panel2); margin: 0;
}
.tiercard:hover { border-color: var(--line-hot) }
.tiercard:has(input:checked) { border-color: var(--green-dim); background: var(--green-fill) }
.tiercard input { width: auto; margin: .3rem 0 0; flex: none }
.tiercard .tname { color: var(--text) }
.tiercard .thelp { display: block; color: var(--muted); font-size: calc(.8rem * var(--scale)) }

/* ── tabs / nav ──────────────────────────────────────────────── */
.tabs { display: flex; gap: .2rem; border-bottom: 1px solid var(--line); margin: 0 0 1rem; flex-wrap: wrap }
.tab, .tabs a, .nav-link {
  color: var(--muted); font-size: var(--fs-sm); padding: .4rem .7rem;
  border: 1px solid transparent; border-bottom: 0; border-radius: var(--r-sm) var(--r-sm) 0 0;
}
.tab:hover, .tabs a:hover, .nav-link:hover { color: var(--green); text-decoration: none }
.tab.active, .tabs a.active, .nav-link.active { color: var(--green); border-color: var(--line); background: var(--panel) }

/* site chrome: header bar, footer, status line, portal topbar */
.site-header, .topbar {
  border-bottom: 1px solid var(--line); background: var(--panel);
  padding: .5rem 0; margin-bottom: 1.6rem;
}

/* The bar rides the top of the viewport, at every width: the brand, the nav,
   the login and the theme switch are the controls a reader reaches for
   mid-page, and the narrower the screen the more of the page a scroll costs
   them. `.topbar` — the operator console's own bar — is left in the flow on
   purpose: this rule is about the shared customer chrome, and the console is
   not a surface a visitor scrolls.

   Sticky, and nothing else. No z-index, deliberately: `.scan` (the fixed veil)
   sits at z-index 9 and must keep painting over the bar, and a positioned
   element with `z-index: auto` already paints above the page's static content,
   so nothing in `main` can cover the bar on its own. An explicit z-index here
   would also turn the bar into a stacking context and trap the nav submenus
   inside it — under the veil, which is the one thing that comment above the
   veil forbids. */
.site-header { position: sticky; top: 0; }
.site-header .container, .topbar-inner { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.brand, .navbar-brand, .topbar-brand { color: var(--text); font-size: var(--fs-sm); letter-spacing: .06em; text-decoration: none }
.brand:hover, .topbar-brand:hover { color: var(--green); text-decoration: none }
.site-nav, .topbar-links, .navbar-links { display: flex; flex-wrap: wrap; gap: .1rem; margin-left: auto; align-items: center }
/* a page-top row for a surface with no bar of its own (the portal's
   sub-pages): something on the left, the theme switch on the right. */
.pagebar { display: flex; align-items: center; gap: 1rem; margin: 0 0 1.25rem }
.pagebar p { margin: 0 }
.pagebar .theme-toggle { margin-left: auto }

.topbar-link, .topbar-cta, .nav-cta {
  color: var(--muted); font-size: var(--fs-sm); padding: .3rem .6rem;
  border: 1px solid transparent; border-radius: var(--r-sm);
}
.topbar-link:hover, .nav-cta:hover { color: var(--green); border-color: var(--line); text-decoration: none }
.topbar-link.active, .nav-link.active { color: var(--green) }
.nav-cta, .topbar-cta { color: var(--green); border-color: var(--green-dim) }
.topbar-user { color: var(--muted); font-size: var(--fs-xs) }
.topbar-auth { display: flex; gap: .4rem; align-items: center; margin-left: .5rem }

.site-footer, .footer {
  border-top: 1px solid var(--line); margin-top: 3rem; padding: 1.2rem 0 1.6rem;
  color: var(--muted); font-size: var(--fs-xs); text-align: center;
}
.site-footer a, .footer a { color: var(--muted) }
.site-footer a:hover, .footer a:hover { color: var(--green); text-decoration: none }

/* The status line closes the document: it is the last block on the page, not a
   rail. It used to be `position: sticky; bottom: 0`, which pinned a row of
   chrome to the foot of every viewport — on a phone that is a permanent band of
   the screen taken from the reading column, to show a hostname and a clock. The
   bar above is the one that follows the reader; this one is read when they
   reach the end. */
.status-bar {
  display: flex; gap: 1rem; justify-content: space-between; flex-wrap: wrap;
  border-top: 1px solid var(--line); background: var(--panel);
  padding: .3rem .8rem; font-size: var(--fs-xs); color: var(--muted);
}

/* ── page furniture ──────────────────────────────────────────── */
.page-header, .post-header { margin: 0 0 1rem; }
.page-title, .post-title { font-size: calc(1.05rem * var(--scale)); letter-spacing: .18em; text-transform: uppercase; color: var(--green); margin: 0 }
.post-meta, .post-tags { color: var(--muted); font-size: var(--fs-xs); margin-top: .3rem }
.cta-section {
  border: 1px solid var(--line); border-left: 2px solid var(--green-dim);
  border-radius: var(--r); background: var(--panel); padding: 1rem 1.1rem; margin: 2rem 0;
}
.cta-title { font-size: calc(.78rem * var(--scale)); letter-spacing: .14em; text-transform: uppercase; color: var(--cyan); margin: 0 0 .4rem }
.cta-text { color: var(--muted); font-size: var(--fs-sm); margin: 0 0 .8rem }
.ascii-banner { color: var(--dim); font-size: calc(12px * var(--scale)); line-height: 1.1; margin: 0 0 1rem; overflow-x: auto; white-space: pre }
.marquee-container { display: none }

/* ── misc ────────────────────────────────────────────────────── */
details {
  border: 1px solid var(--line); border-radius: var(--r); padding: .6rem .8rem;
  background: var(--panel); margin: 1rem 0;
}
summary { cursor: pointer; color: var(--cyan); font-size: calc(.8rem * var(--scale)); letter-spacing: .1em; text-transform: uppercase }
summary:hover { color: var(--green) }
details p, details li { font-size: var(--fs-sm); color: var(--muted) }
.spinner {
  display: inline-block; width: .85em; height: .85em; border: 2px solid var(--amber);
  border-top-color: transparent; border-radius: 50%; animation: spin 1s linear infinite;
  vertical-align: -2px; margin-right: .35rem;
}
@keyframes spin { to { transform: rotate(360deg) } }
canvas.wire, #wire {
  display: block; width: 100%; height: 200px; border: 1px solid var(--line);
  border-radius: var(--r); image-rendering: pixelated;
  background: radial-gradient(700px 160px at 50% 50%, var(--bg-glow) 0%, var(--bg) 75%);
}
.actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center }
.devhead { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; flex-wrap: wrap }
.flash { animation: flash 1s ease-out 1 }
@keyframes flash { from { background: var(--green-fill) } to { background: var(--panel) } }

@media (max-width: 620px) {
  .big { grid-template-columns: 1fr }
  .kv { grid-template-columns: 1fr; gap: 0 }
  .kv span:nth-child(odd) { margin-top: .4rem }
  canvas.wire, #wire { height: 170px }
}

/* ══ the chrome, in one place ══════════════════════════════════════════════
   The header bar, its footer counterpart and the status line are one design
   worn by two surfaces: ymrtech.com (_layouts/default.html) and the client
   portal (clientsv, internal/api/chrome.html). Both load THIS file, so the
   rules below are the single definition of how that chrome behaves — the
   bar's order, the CSS-only mobile panel, the group menus behind services,
   infra and the portal's account entry.

   Everything outside the site-specific list in assets/css/style.css lives
   here: that file now holds only the embedded icon font and the layout
   overrides the hand-authored content pages need. --------------------------- */

/* --- The theme switch in the header --------------------------------------
   Desktop: last thing in the bar, after the nav tabs. At ≤1200px it joins the
   right-hand cluster with the login CTA and the hamburger — order 3 is the
   hamburger's own order, so DOM order (hamburger, then switch) puts the switch
   at the outer edge, one thumb-reach away. Below 480px the status-line switch
   is hidden by the rule above, which is exactly why this one is in the bar. */
.theme-toggle { order: 5; margin-left: 0.5rem; min-height: 0; padding: 0.3rem 0.55rem; }

@media (max-width: 1200px) {
  .theme-toggle { order: 3; margin-left: 0.6rem; margin-right: 0; min-height: 44px; }
}

/* --- Navigation ----------------------------------------------------------
   Desktop: the whole .site-nav row sits inline (theme.css) with the toggle
   hidden and the login CTA in the row itself. At ≤1200px it collapses into a
   checkbox-driven panel — no JS, no hash fragment, works with scripting off.

   The input must stay a sibling that PRECEDES .site-nav and .nav-toggle-label:
   the open/closed state is the `~` selector below. Reordering is done with
   `order`, not with the DOM, so that stays true. */
.nav-toggle,
.admin-toggle,
.nav-toggle-label,
.admin-toggle-label,
.nav-cta-mobile,
.admin-lock { display: none; }

/* --- The console's own row ------------------------------------------------
   The operator console wears the SAME bar as the site and the client portal —
   one header, one footer, one palette; it links this very file — and under it
   a row of its OWN destinations, which is the one thing the shared bar cannot
   carry (its panes are not the site's pages).

   It is a second LINE inside the bar's container, not a second bar: the
   console is a page of the property, not a property of its own. Desktop gets
   it full width under the bar's row — order 6 places it after the theme switch
   (order 5), so the bar's own row keeps line one to itself. A phone gets it as
   a panel behind the console's OWN hamburger, which sits to the LEFT of the
   site's and wears a lock: an operator's menu must not be mistakable for the
   site's. --------------------------------------------------------------- */
.admin-bar {
  order: 6;
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.1rem;
  margin: 0.45rem 0 0;
  padding-top: 0.45rem;
  border-top: 1px solid var(--line);
}
.admin-brand {
  color: var(--muted);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  margin-right: 0.6rem;
}
.admin-brand:hover { color: var(--green); text-decoration: none; }
/* The lock beside the console's hamburger. Drawn as inline SVG with
   stroke: currentColor, so the palette colours it and no colour is named here;
   display comes from the media query below (none on desktop). */
.admin-lock { order: 2; align-items: center; color: var(--amber); margin-left: 0.5rem; }
.admin-lock svg { display: block; }

/* --- Nav groups: services · infra -----------------------------------------
   Ten links in one row is a wall, and five of them are families rather than
   sections. They are grouped into two dropdowns, so the top row reads
   about · services · infra · notes · contact, plus the portal login.

   The group's own label is a real link to the group's first page (services,
   nix-config) — not a dead `span`: the panel is display:none until it opens, so
   a label that could not take focus would leave every row inside it
   unreachable by keyboard. The panel's first row repeats that same target on
   purpose, so the pointer can aim at either the label or the row.

   Desktop opens the panel on hover AND on :focus-within, so it is reachable
   with the Tab key alone (no JS, matching the mobile panel above). It is
   anchored at top:100% with no gap — a visual gap would be a dead zone the
   pointer crosses on its way from the label into the panel.

   z-index: the panel must clear the fixed scanline veil (.scan, z-index 9) and
   the page content after it. .nav-item-group is only `position: relative` and
   never a stacking context of its own, so this z-index is resolved in the root
   context and wins — without having to lift .site-header above the veil (which
   would strip the veil off the header bar itself). ------------------------- */
.nav-item-group { position: relative; display: flex; align-items: center; }

/* The caret is drawn in CSS rather than taken from the icon registry: it marks
   a submenu's open STATE and flips when that state changes, which a CSS
   triangle does and an svg cannot without a transform rule of its own. The
   registry is for the icons that name a destination. */
.nav-caret {
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 0.4rem;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  opacity: 0.6;
  transition: transform 0.2s;
}

.nav-submenu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  min-width: 10rem;
  flex-direction: column;
  padding: 0.3rem;
  background: var(--panel2);
  /* the live-element border, same token a hovered card takes: the panel is the
     one surface in the bar that has to read as an object, not as the bar. */
  border: 1px solid var(--line-hot);
  border-radius: var(--r-sm);
}

.nav-item-group:hover .nav-submenu,
.nav-item-group:focus-within .nav-submenu { display: flex; }

.nav-item-group:hover > .nav-group-link,
.nav-item-group:focus-within > .nav-group-link { color: var(--green); }

.nav-item-group:hover .nav-caret,
.nav-item-group:focus-within .nav-caret { transform: rotate(180deg); }

/* Rows, not tabs: the theme's tab corners and missing bottom border belong to
   a horizontal row, and these sit stacked inside a panel. */
.nav-submenu .nav-link {
  display: block;
  padding: 0.4rem 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  white-space: nowrap;
}

.nav-submenu .nav-link:hover { background: var(--hover); }

@media (max-width: 1200px) {
  /* The state holder stays in the layout — focusable, no visual box — rather
     than display:none, so the label can still be operated from the keyboard.
     The console's own toggle is the same control. */
  .nav-toggle,
  .admin-toggle {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
  }

  /* login stays one tap away in the header bar, outside the collapsed panel */
  .nav-cta-mobile {
    display: inline-block;
    order: 2;
    margin: 0 0.6rem 0 auto;
    padding: 0.3rem 0.75rem;
    font-size: var(--fs-xs);
  }

  /* the lock rides with the login CTA's cluster, ahead of both hamburgers */
  .admin-lock { display: inline-flex; }

  /* Both hamburgers are one control: the console's sits to the site's LEFT
     (same order, earlier in the DOM) and wears the lock above. */
  .nav-toggle-label,
  .admin-toggle-label {
    display: flex;
    order: 3;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 38px;
    height: 34px;
    padding: 8px 9px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .nav-toggle-label span,
  .admin-toggle-label span {
    display: block;
    width: 100%;
    height: 2px;
    background: var(--green);
    border-radius: 1px;
    transition: transform 0.2s, opacity 0.2s;
  }
  /* the input is the control, the label is the box you can see: put the focus
     ring on the latter. */
  .nav-toggle:focus-visible ~ .nav-toggle-label,
  .admin-toggle:focus-visible ~ .admin-toggle-label {
    outline: 2px solid var(--cyan);
    outline-offset: 2px;
  }
  .nav-toggle:checked ~ .nav-toggle-label span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .nav-toggle:checked ~ .nav-toggle-label span:nth-child(2) { opacity: 0; }
  .nav-toggle:checked ~ .nav-toggle-label span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
  .admin-toggle:checked ~ .admin-toggle-label span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .admin-toggle:checked ~ .admin-toggle-label span:nth-child(2) { opacity: 0; }
  .admin-toggle:checked ~ .admin-toggle-label span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

  .site-nav,
  .admin-bar {
    display: none;
    order: 4;
    flex-basis: 100%;
    width: 100%;
    flex-direction: column;
    /* the shared row rule sets flex-wrap: wrap; on a COLUMN container wrap means
       "spill into another column", so a capped panel would lay the rows that
       don't fit into a second column off to the right — invisible and
       unreachable. The panel scrolls; it never wraps. */
    flex-wrap: nowrap;
    align-items: stretch;
    margin: 0.6rem 0 0;
    border-top: 1px solid var(--line);
    /* The bar is pinned, so the OPEN panel cannot make the header taller than
       the screen: a sticky box taller than the viewport cannot be scrolled to,
       and the rows at its foot (contact, the login row inside the panel) would
       be stranded. The panel scrolls inside itself instead — 6rem is the bar's
       own height with room to spare at every width in this band, and `dvh`
       tracks the phone's disappearing toolbars, so the rows are always
       reachable. `contain` keeps a gesture that reaches the panel's end from
       scrolling the article behind it. */
    max-height: calc(100vh - 6rem);
    max-height: calc(100dvh - 6rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* the console's panel is the same object as the site's, minus the bar's own
     hairline spacing: it starts where the site's panel ends. */
  .admin-bar { gap: 0; padding-top: 0; }
  .nav-toggle:checked ~ .site-nav { display: flex; }
  .admin-toggle:checked ~ .admin-bar { display: flex; }

  /* The rows keep their own height: a capped column flex container shrinks its
     items to fit, and the text then overflows the shrunken boxes and reads over
     the next row. The panel is the thing that moves, not the rows. */
  .site-nav > * { flex: 0 0 auto; }

  .site-nav .nav-link,
  .admin-bar .topbar-link {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    min-height: 46px;
    padding: 0.7rem 0.4rem;
    /* rides theme.css's --scale, like every other size in this file */
    font-size: calc(0.95rem * var(--scale));
    border-bottom: 1px solid var(--line);
    border-radius: 0;   /* the theme's tab corners are for a horizontal row */
  }
  .admin-bar .admin-brand {
    padding: 0.8rem 0.4rem 0.2rem;
    margin: 0;
    border-bottom: 0;
  }

  /* the same CTA is already in the mobile header bar — don't show it twice */
  .site-nav .nav-cta-panel { display: none; }

  /* Groups unfold in place. There is no hover on a touch screen, so the panel
     is not a thing to open: the children are simply indented under a heading.
     The label drops to a dim upper-case caption so the rows below it read as
     its contents — it is still a link, it just no longer competes visually with
     the pages it contains. */
  .nav-item-group { display: block; width: 100%; }
  .site-nav .nav-group-link {
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: var(--fs-xs);
    color: var(--muted);
    min-height: 0;
    padding: 0.8rem 0.4rem 0.2rem;
    border-bottom: 0;
  }
  .nav-group-link .nav-caret { display: none; }
  .nav-submenu {
    display: flex;
    position: static;
    min-width: 0;
    margin-left: 0.55rem;
    padding: 0;
    background: none;
    border: 0;
    border-left: 1px solid var(--line);
    border-radius: 0;
  }
  .site-nav .nav-submenu .nav-link { padding-left: 0.9rem; }
}

@media (prefers-reduced-motion: reduce) {
  .nav-toggle-label span,
  .admin-toggle-label span { transition: none; }
}

/* --- Status bar ----------------------------------------------------------
   theme.css provides the bar; the site colours its own three segments and
   drops the middle one on narrow phones, where three segments wrap into noise. */
.status-bar .status-left { color: var(--green); }
.status-bar .status-right { color: var(--cyan); }

@media (max-width: 480px) {
  .status-bar .status-mid { display: none; }
}

/* ── the signed-in menu: profile · settings · subscription · billing ───────
   The portal's account entry is the same .nav-item-group/.nav-submenu the
   site's services and infra menus use, with captioned sections instead of one
   flat list: first what the client came to change, then what they pay. A
   caption is not a link — the rows inside it carry the targets. */
.nav-submenu-head {
  display: block;
  padding: 0.55rem 0.75rem 0.15rem;
  color: var(--muted);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.12em;
}
.nav-submenu-head:not(:first-child) {
  margin-top: 0.35rem;
  border-top: 1px solid var(--line);
}
.nav-submenu-note {
  display: block;
  padding: 0 0.75rem 0.35rem;
  color: var(--muted);
  font-size: var(--fs-xs);
}
/* Inside a panel the active row is marked by the signal colour AND a light
   palette fill (--active), so the page you are on reads as a lit tile in
   either theme: the bordered active tab (above) belongs to the tab row, not
   to a dropdown. */
.nav-submenu .nav-link.active {
  color: var(--green);
  background: var(--active);
  border-color: transparent;
}

