/* ============================================================
   Firehose - Centralized Logging and Correlation
   Console aesthetic: monospace collector pane, dense device rows.
   Light / Dark / Auto plus High-Visibility, per the Izzy standard.

   THEMING IS TOKEN-ONLY. The :root block below declares every colour,
   font and dimension. The single body.dark block re-declares those
   same properties and nothing else, so every rule downstream is
   theme-agnostic by construction. Responsive breakpoints re-declare
   tokens as well, rather than restating layout.

   No em dashes or en dashes anywhere.
   ============================================================ */

:root {
  /* Brand. Ohio University primary and secondary palette. `../iz.css` is the
     canonical source and is linked ahead of this sheet; these mirror it with
     the authoritative hexes so the page still renders correctly if the shared
     sheet is ever missing under file://. Keep the two in step. */
  --green:        #00694E;   /* Cutler Green   */
  --green-dark:   #024230;   /* Under the Elms */
  --white:        #FFFFFF;   /* Cupola White   */
  --putnam:       #756E65;
  --sycamore:     #E7ECC3;
  --marigold:     #AA8A00;

  /* Surfaces. Semantic names, not colour names: use --fh-panel when the
     intent is "panel surface", never --white. */
  --fh-bg:        #f4f5f2;
  --fh-panel:     #ffffff;
  --fh-panel-2:   #f0f1ec;
  --fh-ink:       #16181a;
  --fh-ink-mute:  #5d6169;
  --fh-line:      #cfd3cb;
  --fh-accent:    var(--green);
  --fh-accent-2:  var(--green-dark);
  --fh-hover:     #eef2e2;
  --fh-sel:       #dfe9d4;

  /* State. Severity is the load-bearing one, and the eight levels are
     collapsed to four bands for colour: a student reads the word, not
     the hue. Each band differs in LIGHTNESS as well as hue, so the
     common colour vision deficiencies still separate them, and the
     severity key is always present in the row so colour is never the
     only channel. */
  --fh-sev-emerg:  #7d1d16;
  --fh-sev-high:   #b3261e;
  --fh-sev-warn:   #9a6700;
  --fh-sev-notice: #40616f;
  --fh-sev-info:   #5d6169;
  --fh-sev-debug:  #8a8f96;

  --fh-ok:        #1f6f43;
  --fh-warn:      #9a6700;
  --fh-bad:       #b3261e;
  --fh-error-bg:  #fce6e6;
  --fh-quiet:     #9aa0a6;

  /* Type */
  --fh-mono: ui-monospace, "Cascadia Mono", "Consolas", "DejaVu Sans Mono", monospace;
  --fh-body: "Segoe UI", Arial, sans-serif;
  --fh-fs: 13px;

  /* Layout */
  --fh-row-h: 22px;
  --fh-devrow-h: 34px;
  --fh-toolbar-h: 48px;
  --fh-pane-gap: 6px;
  --fh-devices-w: minmax(220px, 1fr);
  --fh-collector-w: minmax(380px, 2.4fr);
  --fh-side-w: minmax(260px, 1.2fr);
}

/* ── Dark mode ───────────────────────────────────────────────
   One block, re-declaring tokens and nothing else. `auto` is
   resolved in JavaScript against matchMedia and rides on body.dark,
   which is ENE's arrangement and the one the standard names. It
   declares the dark palette ONCE, where html[data-theme] would force
   it to be written twice and let the two copies drift. */
body.dark {
  --fh-bg:        #101314;
  --fh-panel:     #171b1d;
  --fh-panel-2:   #1e2325;
  --fh-ink:       #e6e9e4;
  --fh-ink-mute:  #9aa2a6;
  --fh-line:      #2c3336;
  --fh-accent:    #4fbf95;
  --fh-accent-2:  #7fd9b6;
  --fh-hover:     #23292b;
  --fh-sel:       #24382f;

  --fh-sev-emerg:  #ff8a7e;
  --fh-sev-high:   #ff6b5e;
  --fh-sev-warn:   #e0a83a;
  --fh-sev-notice: #7fb3c8;
  --fh-sev-info:   #9aa2a6;
  --fh-sev-debug:  #6d747a;

  --fh-ok:        #57c98a;
  --fh-warn:      #e0a83a;
  --fh-bad:       #ff6b5e;
  --fh-error-bg:  #3a1f1f;
  --fh-quiet:     #666d72;

  --sycamore:     #2a352a;
  --marigold:     #E3B341;
  --putnam:       #9DA39B;
}

/* ── High-Visibility ─────────────────────────────────────────
   Scales the whole layout through the two size tokens rather than
   restyling each rule. For a projector, or for a student who needs it. */
html.fh-hivis {
  --fh-fs: 17px;
  --fh-row-h: 30px;
  --fh-devrow-h: 44px;
}
html.fh-hivis body {
  --fh-ink: #000000;
  --fh-ink-mute: #26292c;
  --fh-line: #000000;
  font-weight: 600;
}
html.fh-hivis body.dark {
  --fh-ink: #ffffff;
  --fh-ink-mute: #e2e6e8;
  --fh-line: #ffffff;
  --fh-bg: #000000;
  --fh-panel: #000000;
  --fh-panel-2: #141414;
}
html.fh-hivis .fh-pane { border-width: 2px; }

/* ── Base ────────────────────────────────────────────────────── */
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; padding: 0; }
body {
  background: var(--fh-bg);
  color: var(--fh-ink);
  font-family: var(--fh-body);
  font-size: var(--fh-fs);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
button, input { font-family: inherit; font-size: inherit; }
[hidden] { display: none !important; }

/* ── Hamburger and its drawer ────────────────────────────────
   Matched to ENE's, which is the reference implementation for this
   chrome, and ported onto Firehose's tokens so it themes with
   everything else. iz.css is still linked for the shared brand
   variables; its own .ect-nav-menu panel is deliberately NOT used,
   because ENE's drawer is a light surface with pill state badges
   rather than a dark green panel of links. */
.fh-hamburger { position: relative; flex: 0 0 auto; }

/* The hamburger is the odd one out on the toolbar on purpose: Putnam
   border, normal weight, so the menu reads as chrome rather than as one
   more action competing with New and Run. */
.btn-tertiary {
  background: transparent; color: var(--putnam);
  border: 2px solid var(--putnam); padding: 4px 13px;
  border-radius: 6px; cursor: pointer; font-weight: normal; font-size: 1.15rem;
  line-height: 1;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.btn-tertiary:hover { background: var(--sycamore); color: var(--marigold); }

.dropdown-drawer {
  position: absolute; z-index: 110;
  background: var(--fh-panel);
  border: 2px solid var(--fh-accent);
  border-radius: 8px;
  padding: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  min-width: 280px;
  max-height: calc(100vh - var(--fh-toolbar-h) - 24px);
  overflow-y: auto;
}
body.dark .dropdown-drawer { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55); }

.drawer-item {
  display: block; width: 100%; text-align: left;
  background: transparent; border: none;
  padding: 8px 10px; border-radius: 4px;
  cursor: pointer; color: var(--fh-accent-2); font-weight: bold;
  font-family: inherit; font-size: var(--fh-fs);
}
/* Without this the HTML hidden attribute does not actually hide a
   .drawer-item, because the class selector's display: block beats the UA
   stylesheet's [hidden] rule at equal specificity. ENE records the same
   trap. */
.drawer-item[hidden] { display: none; }
.drawer-item:hover { background: var(--sycamore); }

.drawer-item.drawer-item-danger { color: var(--fh-bad); }
.drawer-item.drawer-item-danger:hover { background: var(--fh-error-bg); color: var(--fh-bad); }

.drawer-item.drawer-toggle {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
/* The state rides in a pill on the row, so no checkmark glyph is needed and
   the label never has to say "on" twice. */
.drawer-toggle-state {
  font-weight: bold; font-size: 12px;
  padding: 2px 8px; border-radius: 999px;
  background: var(--fh-line); color: var(--putnam);
  border: 1px solid var(--fh-line);
}
.drawer-toggle.is-on .drawer-toggle-state {
  background: var(--fh-accent); color: var(--white);
  border-color: var(--fh-accent-2);
}
/* One of the handful of legitimate targeted dark overrides: white on the
   lifted dark-mode green is about 2:1, which fails at pill size. The
   contrast math genuinely differs by theme, so it cannot come from a token. */
body.dark .drawer-toggle.is-on .drawer-toggle-state { color: #0b1a14; }

.dropdown-drawer .drawer-section {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--putnam); font-weight: bold;
  padding: 8px 10px 4px; margin-top: 4px;
  border-top: 1px solid var(--fh-line);
}
.dropdown-drawer .drawer-section:first-child { border-top: none; margin-top: 0; }

/* ── Drawer popouts ──────────────────────────────────────────
   A parent row that expands a side submenu. The popout opens to the LEFT of
   the drawer and grows downward from its parent row: the drawer anchors to
   the right edge of the hamburger, so a left-side popout is the direction
   that stays on screen. */
.drawer-item.drawer-popout-parent {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.drawer-popout-arrow {
  display: inline-block;
  font-size: 11px; color: var(--putnam);
  transition: transform 0.15s ease;
}
/* Open, the submenu is to the left, so the arrow turns to point at it. */
.drawer-item.drawer-popout-parent.expanded .drawer-popout-arrow { transform: rotate(180deg); }

.drawer-popout {
  display: none;
  /* position: fixed, because the parent drawer scrolls and overflow-y: auto
     implicitly clips overflow-x too. The top and right are written by JS from
     the parent row's rectangle so the popout snaps beside it. */
  position: fixed;
  background: var(--fh-panel);
  border: 2px solid var(--fh-accent);
  border-radius: 8px;
  padding: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
  min-width: 220px;
  max-height: calc(100vh - var(--fh-toolbar-h) - 24px);
  overflow-y: auto;
  z-index: 112;
}
body.dark .drawer-popout { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55); }
/* Visibility rides a class on the popout itself, because the popout is a
   SIBLING of its parent row rather than a child of it. See makePopout: a
   button cannot legally contain buttons, and the nesting also shadowed the
   click dispatch for every row inside a submenu. */
.drawer-popout.is-open { display: block; }
/* Children read a touch lighter than top-level rows, since the parent's
   hover already puts them in focus. */
.drawer-popout .drawer-item { font-weight: normal; }
.drawer-popout .drawer-item-desc {
  display: block; font-size: 11px; font-weight: normal; color: var(--putnam);
  white-space: normal; line-height: 1.3; margin-top: 1px;
}
.drawer-popout .drawer-item.is-current { background: var(--sycamore); }

/* ── Toolbar ─────────────────────────────────────────────────── */
.fh-toolbar {
  flex: 0 0 auto;
  min-height: var(--fh-toolbar-h);
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 0 12px;
  background: var(--fh-panel);
  border-bottom: 2px solid var(--fh-accent);
  position: relative; z-index: 10;
}
.fh-toolbar .brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.brand-mark { width: 26px; height: 26px; border-radius: 4px; flex: 0 0 auto; }
.brand-text { font-size: 1.25rem; font-weight: bold; color: var(--fh-accent-2); }
body.dark .brand-text { color: var(--fh-accent); }
.brand-text-short { display: none; }

.fh-scale { display: flex; flex-direction: column; line-height: 1.15; margin-left: 6px; min-width: 0; }
.fh-scale-tier { font-weight: bold; color: var(--fh-ink); white-space: nowrap; }
.fh-scale-sub { font-size: 0.82em; color: var(--fh-ink-mute); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }

.fh-window {
  flex: 0 1 auto; padding: 3px 12px; border-radius: 12px;
  background: var(--fh-panel-2); border: 1px solid var(--fh-line);
  color: var(--fh-ink-mute); font-family: var(--fh-mono); font-size: 0.88em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fh-window:empty { display: none; }

.fh-toolbar .tools { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.fh-received { color: var(--fh-ink-mute); font-variant-numeric: tabular-nums; white-space: nowrap; }
.fh-code-box { display: flex; align-items: center; gap: 5px; }
.fh-code-label { color: var(--fh-ink-mute); font-size: 0.85em; }
.fh-code-input {
  width: 128px; padding: 4px 8px; border-radius: 5px;
  border: 1px solid var(--fh-line); background: var(--fh-panel-2); color: var(--fh-ink);
  font-family: var(--fh-mono); text-transform: uppercase;
}
.fh-code-input:focus { outline: 2px solid var(--fh-accent); outline-offset: 1px; }

.fh-btn {
  padding: 5px 13px; border-radius: 6px; cursor: pointer;
  border: 2px solid var(--fh-accent); background: transparent;
  color: var(--fh-accent-2); font-weight: 600;
}
body.dark .fh-btn { color: var(--fh-accent); }
.fh-btn:hover { background: var(--fh-accent); color: var(--white); }

.fh-linkbtn {
  border: 1px solid transparent; background: transparent; cursor: pointer;
  color: var(--fh-ink-mute); padding: 1px 6px; border-radius: 4px; font-size: 0.9em;
}
.fh-linkbtn:hover { color: var(--fh-accent-2); border-color: var(--fh-line); }
body.dark .fh-linkbtn:hover { color: var(--fh-accent); }
.fh-linkbtn.on { color: var(--white); background: var(--fh-accent); border-color: var(--fh-accent); }

/* ── Panes ───────────────────────────────────────────────────── */
.fh-main {
  flex: 1 1 auto; min-height: 0;
  display: grid;
  grid-template-columns: var(--fh-devices-w) var(--fh-collector-w) var(--fh-side-w);
  gap: var(--fh-pane-gap); padding: var(--fh-pane-gap);
}
.fh-pane {
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
  background: var(--fh-panel);
  border: 1px solid var(--fh-line); border-radius: 6px; overflow: hidden;
}
.fh-pane-head {
  flex: 0 0 auto; display: flex; align-items: center; gap: 8px;
  padding: 4px 8px; background: var(--fh-panel-2);
  border-bottom: 1px solid var(--fh-line);
}
.fh-pane-title { font-weight: bold; color: var(--fh-accent-2); }
body.dark .fh-pane-title { color: var(--fh-accent); }
.fh-pane-meta { color: var(--fh-ink-mute); font-size: 0.85em; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Device pane ─────────────────────────────────────────────── */
.fh-device-filter { flex: 0 0 auto; padding: 5px 7px; border-bottom: 1px solid var(--fh-line); }
.fh-device-input {
  width: 100%; padding: 4px 7px; border-radius: 5px;
  border: 1px solid var(--fh-line); background: var(--fh-panel-2); color: var(--fh-ink);
}
.fh-device-input:focus { outline: 2px solid var(--fh-accent); outline-offset: 1px; }
.fh-device-scroll { flex: 1 1 auto; overflow-y: auto; position: relative; min-height: 0; }
.fh-device-spacer { position: absolute; top: 0; left: 0; width: 1px; }
.fh-device-rows { position: absolute; top: 0; left: 0; right: 0; }

.fh-dev {
  height: var(--fh-devrow-h);
  display: flex; align-items: center; gap: 7px;
  padding: 0 8px; cursor: pointer;
  border-bottom: 1px solid var(--fh-line);
}
.fh-dev:hover { background: var(--fh-hover); }
.fh-dev.sel { background: var(--fh-sel); }
.fh-dev-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; background: var(--fh-ok); }
.fh-dev-dot.warn { background: var(--fh-warn); }
.fh-dev-dot.quiet { background: var(--fh-quiet); }
.fh-dev-dot.off { background: transparent; border: 2px solid var(--fh-quiet); }
.fh-dev-text { min-width: 0; flex: 1 1 auto; line-height: 1.15; }
.fh-dev-name { font-family: var(--fh-mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
.fh-dev-sub { color: var(--fh-ink-mute); font-size: 0.8em; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; display: block; }
.fh-dev-site { color: var(--fh-ink-mute); font-size: 0.78em; flex: 0 0 auto; }

/* ── Query bar ───────────────────────────────────────────────── */
.fh-querybar { flex: 0 0 auto; display: flex; gap: 6px; padding: 6px 7px;
  border-bottom: 1px solid var(--fh-line); }
.fh-query-input {
  flex: 1 1 auto; min-width: 0; padding: 5px 9px; border-radius: 5px;
  border: 1px solid var(--fh-line); background: var(--fh-panel-2); color: var(--fh-ink);
  font-family: var(--fh-mono);
}
.fh-query-input:focus { outline: 2px solid var(--fh-accent); outline-offset: 1px; }
.fh-query-msg { flex: 0 0 auto; padding: 0 9px; font-family: var(--fh-mono);
  font-size: 0.86em; white-space: pre-wrap; color: var(--fh-ink-mute); }
.fh-query-msg:empty { display: none; }
.fh-query-msg.err { color: var(--fh-bad); padding: 4px 9px; }
.fh-fixbtn {
  margin-left: 6px; padding: 1px 7px; border-radius: 4px; cursor: pointer;
  border: 1px solid var(--fh-accent); background: transparent; color: var(--fh-accent-2);
  font-family: var(--fh-mono); font-size: 0.95em;
}
body.dark .fh-fixbtn { color: var(--fh-accent); }
.fh-fixbtn:hover { background: var(--fh-accent); color: var(--white); }

/* ── Collector pane ──────────────────────────────────────────── */
/* The spacer is a one-pixel strut whose HEIGHT is the full virtual list, so
   the scrollbar is honest; the rows container is translated to the offset and
   holds only the visible window. At 700,000 rows this is not an optimisation,
   it is the difference between a pane and a hung tab. */
.fh-log-scroll {
  flex: 1 1 auto; overflow: auto; position: relative; min-height: 0;
  font-family: var(--fh-mono); font-size: calc(var(--fh-fs) * 0.88);
}
.fh-log-spacer { position: absolute; top: 0; left: 0; width: 1px; }
/* No `right: 0`: a raw wire line is allowed to be wider than the pane and the
   container scrolls sideways to meet it. Truncating one with an ellipsis would
   hide the end of the message, which on a syslog line is usually the half that
   matters. The decoded grid below still truncates per cell, because that view
   exists to be scanned rather than read. */
.fh-log-rows { position: absolute; top: 0; left: 0; min-width: 100%; }

.fh-log-row {
  height: var(--fh-row-h); line-height: var(--fh-row-h);
  padding: 0 8px; white-space: pre;
  width: max-content; min-width: 100%;
  border-left: 3px solid transparent; cursor: default;
}
.fh-log-row:hover { background: var(--fh-hover); }
.fh-log-row.sel { background: var(--fh-sel); }
.fh-log-row.sev-0, .fh-log-row.sev-1 { color: var(--fh-sev-emerg); border-left-color: var(--fh-sev-emerg); }
.fh-log-row.sev-2, .fh-log-row.sev-3 { color: var(--fh-sev-high); border-left-color: var(--fh-sev-high); }
.fh-log-row.sev-4 { color: var(--fh-sev-warn); border-left-color: var(--fh-sev-warn); }
.fh-log-row.sev-5 { color: var(--fh-sev-notice); }
.fh-log-row.sev-6 { color: var(--fh-ink); }
.fh-log-row.sev-7 { color: var(--fh-sev-debug); }

.fh-grid-row { display: flex; gap: 0; }
.fh-cell { padding: 0 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 0 auto; }
.fh-cell-arrival, .fh-cell-stated { width: 74px; color: var(--fh-ink-mute); }
.fh-cell-host { width: 130px; }
.fh-cell-facility { width: 68px; color: var(--fh-ink-mute); }
.fh-cell-severity { width: 62px; font-weight: 600; }
.fh-cell-app { width: 84px; color: var(--fh-ink-mute); }
.fh-cell-message { flex: 1 1 auto; width: auto; }

/* Columns size to their content rather than spreading across the pane: a two
   column aggregate with the count pushed to the far right is measurably harder
   to read than the same table set tight. The wrapper scrolls when a wide
   result needs it. */
.fh-tablewrap { padding: 4px 0; overflow-x: auto; }
.fh-table { border-collapse: collapse; width: auto; font-family: var(--fh-mono); }
.fh-table th, .fh-table td { padding-right: 26px; }
.fh-table th {
  text-align: left; padding: 3px 9px; font-weight: bold;
  color: var(--fh-accent-2); border-bottom: 2px solid var(--fh-line);
  position: sticky; top: 0; background: var(--fh-panel);
}
body.dark .fh-table th { color: var(--fh-accent); }
.fh-table td { padding: 2px 9px; border-bottom: 1px solid var(--fh-line);
  white-space: nowrap; font-variant-numeric: tabular-nums; }
.fh-table tr:hover td { background: var(--fh-hover); }

.fh-empty { padding: 18px 12px; color: var(--fh-ink-mute); font-family: var(--fh-body); }
.fh-empty strong { color: var(--fh-ink); }

.fh-log-foot {
  flex: 0 0 auto; padding: 3px 9px; border-top: 1px solid var(--fh-line);
  background: var(--fh-panel-2); color: var(--fh-ink-mute); font-size: 0.82em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fh-log-foot:empty { display: none; }

/* ── Side pane ───────────────────────────────────────────────── */
.fh-tabs { flex: 0 0 auto; display: flex; border-bottom: 1px solid var(--fh-line);
  background: var(--fh-panel-2); }
.fh-tab {
  flex: 1 1 auto; padding: 6px 4px; cursor: pointer; border: none;
  background: transparent; color: var(--fh-ink-mute); font-weight: 600;
  border-bottom: 3px solid transparent;
}
.fh-tab:hover { color: var(--fh-ink); }
.fh-tab-on { color: var(--fh-accent-2); border-bottom-color: var(--fh-accent); }
body.dark .fh-tab-on { color: var(--fh-accent); }
.fh-tabbody { flex: 1 1 auto; overflow: auto; padding: 9px 11px; min-height: 0; }

.fh-kv { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; align-items: baseline; }
.fh-kv dt { color: var(--fh-ink-mute); font-size: 0.88em; white-space: nowrap; }
.fh-kv dd { margin: 0; font-family: var(--fh-mono); overflow-wrap: anywhere; }
.fh-detail-h { font-weight: bold; color: var(--fh-accent-2); margin: 12px 0 5px; }
body.dark .fh-detail-h { color: var(--fh-accent); }
.fh-detail-h:first-child { margin-top: 0; }
.fh-raw {
  font-family: var(--fh-mono); font-size: 0.86em; white-space: pre-wrap;
  overflow-wrap: anywhere; background: var(--fh-panel-2);
  border: 1px solid var(--fh-line); border-radius: 4px; padding: 6px 8px; margin: 4px 0 0;
}
.fh-note { color: var(--fh-ink-mute); font-size: 0.9em; }
.fh-soon { color: var(--fh-ink-mute); }
.fh-soon code { font-family: var(--fh-mono); color: var(--fh-ink); }

/* ── Modal ───────────────────────────────────────────────────── */
.fh-modal {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.fh-modal-card {
  background: var(--fh-panel); color: var(--fh-ink);
  border: 2px solid var(--fh-accent); border-radius: 8px;
  max-width: 780px; width: 100%; max-height: 86vh;
  display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}
.fh-modal-head {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 9px 13px; background: var(--fh-panel-2);
  border-bottom: 1px solid var(--fh-line);
  font-weight: bold; color: var(--fh-accent-2);
}
body.dark .fh-modal-head { color: var(--fh-accent); }
.fh-modal-body { flex: 1 1 auto; overflow: auto; padding: 13px 16px; line-height: 1.5; }
.fh-modal-body h2 { color: var(--fh-accent-2); margin: 0 0 8px; font-size: 1.5rem; }
.fh-modal-body h3 { color: var(--fh-accent-2); margin: 16px 0 6px; }
body.dark .fh-modal-body h2, body.dark .fh-modal-body h3 { color: var(--fh-accent); }
.fh-modal-body h2:first-child, .fh-modal-body h3:first-child { margin-top: 0; }
.fh-modal-body a { color: var(--fh-accent-2); }
body.dark .fh-modal-body a { color: var(--fh-accent); }
.fh-modal-body p { margin: 0 0 9px; }
.fh-modal-body ul { margin: 0 0 9px; padding-left: 22px; }
.fh-modal-body li { margin: 2px 0; }
.fh-pre {
  font-family: var(--fh-mono); font-size: 0.88em; white-space: pre-wrap;
  background: var(--fh-panel-2); border-left: 3px solid var(--fh-accent);
  border-radius: 4px; padding: 9px 11px; margin: 0 0 10px; overflow-x: auto;
}
.fh-examples { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.fh-example {
  font-family: var(--fh-mono); font-size: 0.88em; cursor: pointer;
  padding: 3px 9px; border-radius: 5px;
  border: 1px solid var(--fh-line); background: var(--fh-panel-2); color: var(--fh-ink);
}
.fh-example:hover { border-color: var(--fh-accent); color: var(--fh-accent-2); }
body.dark .fh-example:hover { color: var(--fh-accent); }
.fh-attrib { color: var(--fh-ink-mute); font-size: 0.92em; }
.fh-copybox {
  width: 100%; min-height: 82px; font-family: var(--fh-mono); font-size: 0.86em;
  background: var(--fh-panel-2); color: var(--fh-ink);
  border: 1px solid var(--fh-line); border-radius: 4px; padding: 7px;
}

/* ── Pane switcher ───────────────────────────────────────────── */
/* Declared BEFORE the breakpoints that reveal it. Written after them, this
   base rule would win on source order at equal specificity and the switcher
   would stay hidden at exactly the width where every pane is also hidden,
   leaving a blank page. */
.fh-paneswitch { display: none; flex: 0 0 auto; gap: 4px; padding: 4px 6px;
  background: var(--fh-panel-2); border-top: 1px solid var(--fh-line); }
.fh-paneswitch button {
  flex: 1 1 auto; padding: 7px 4px; cursor: pointer; font-weight: 600;
  border: 1px solid var(--fh-line); border-radius: 5px;
  background: var(--fh-panel); color: var(--fh-ink-mute);
}
.fh-paneswitch button.on { background: var(--fh-accent); color: var(--white); border-color: var(--fh-accent); }

/* ── Responsive ──────────────────────────────────────────────── */
/* Breakpoints RE-DECLARE TOKENS rather than restating the layout. The narrow
   case collapses to a pane switcher, which is one extra rule rather than a
   second stylesheet. */
@media (max-width: 1180px) {
  :root {
    --fh-devices-w: minmax(180px, 0.9fr);
    --fh-side-w: minmax(220px, 1fr);
    --fh-fs: 12px;
  }
  .fh-cell-host { width: 110px; }
  .fh-cell-app { width: 70px; }
}

@media (max-width: 900px) {
  :root { --fh-toolbar-h: 42px; }
  .brand-text-full { display: none; }
  .brand-text-short { display: inline; }
  .fh-scale-sub { display: none; }
  .fh-window { display: none; }
}

@media (max-width: 760px) {
  .fh-main { grid-template-columns: 1fr; grid-template-rows: 1fr; }
  /* One pane at a time, chosen by the switcher. */
  .fh-pane { display: none; }
  body[data-pane="devices"] #fh-device-pane,
  body[data-pane="collector"] #fh-log-pane,
  body[data-pane="side"] #fh-side-pane { display: flex; }
  .fh-paneswitch { display: flex; }
}
