/* instruments.css — H33 live debug instruments panel: its column, and its
   own content styling.

   Kept separate from style.css, following the hwcontrol.css precedent (that
   file stays a byte-for-byte copy of the mux UI's, and this page's layout is
   not the mux UI's). Everything below `Blocks` is .debug-only surface:
   invisible unless body.debug-mode is set (H9/H22). The page grid at the top
   is NOT debug-only — it is the page's own frame, and it must be identical
   whether the panel is showing or not, which is half of why the stream never
   moves.

   ── The page grid (owner's layout, 2026-09-02) ─────────────────────────
   A table of three columns and two rows: the stream is row 1 of columns 1+2,
   the two existing section columns are row 2, and this panel is column 3
   spanning both rows — its own full-height column on the right.

   WHY THE GRID LIVES IN THIS FILE. It exists for one reason: to give the
   instruments panel a column. Nothing else on the page asked for it, style.css
   is the shared/verbatim sheet, and putting a rule there that only makes
   sense with #instr-panel present would spread H33 across two sheets. If a
   second full-height column is ever wanted, this is the rule to generalise.

   TWO THINGS THIS GRID GUARANTEES BY CONSTRUCTION, not by being careful:

   1. THE PANEL CANNOT TOUCH THE STREAM OR THE SECTIONS. Columns 1+2 are
      merged for both rows — `.page-grid-main` holds the stream card and the
      whole `.two-col` block, stacked, in ONE cell — instead of being four
      cells of a literal 3x2 grid. In a literal 3x2 grid the panel spans both
      row tracks, so a panel taller than stream+sections grows those tracks:
      the stream's cell stretches and the section columns move down. That is
      the same height coupling this file's history is about, rebuilt out of
      grid rows. Nested, there is no shared track to grow.

   2. THE MERGED STREAM CELL'S CENTRE IS THE PAGE'S CENTRE. Column 1 is empty
      and exists only to balance column 3. Both are `minmax(0, 1fr)` in one
      template, so they are equal BY DECLARATION — nothing measures anything.
      That is the difference from the pre-P8 "mirror spacer", which was a flex
      sibling inside the stream's own box, sized from whatever width the panel
      had taken; it fed the panel's width back into the stream's row, which is
      how a deployed panel could crush the stream from 760x612 to 116x141 px
      (the video itself 74x56) and lift the whole `.two-col` block 398 px up
      the page while the centring check stayed green. Measured, both states, in
      docs/sessions/2026_09_02_team11-webui-instruments/evidence/P8/.
      `minmax(0, 1fr)`'s zero minimum also means the panel's own content can
      never widen its track — so the width theft that once moved two
      left-column cards 15 px is not reachable from here either.

   Both are asserted, in every state, by evidence/P3/tools/
   drive_panel_acceptance.py, and both assertions are shown to FAIL on real
   measured geometry of the defect in evidence/P9/tools/. */

.page-grid {
  display: grid;
  gap: 0 1.25rem;
  width: 100%;
  /* Narrow form (the default): one column. The panel keeps its own cell but
     stacks below the sections rather than being squeezed into a sliver, and
     the single track is still centred, so the stream's centre is still the
     page's centre. */
  grid-template-columns: minmax(0, 760px);
  max-width: 760px;
}
/* Column 1 has no content and does not need an element — a grid track exists
   because the template says so. The element is here so that auto-placement
   puts `.page-grid-main` in column 2 and the panel in column 3 without either
   naming a track number, and so the empty column is visible to anyone reading
   the HTML. It is display:none in the narrow form, where it would otherwise
   occupy a whole empty row. */
.page-grid-mirror { display: none; }
/* min-width:0 on both cells: a grid item's automatic minimum size is its
   content's min-content width, which would let a long unbreakable string
   inside either cell push the whole grid wider than the page. */
.page-grid-main,
.page-grid-instr { min-width: 0; }

/* 1400px is where column 3 still has ~300 px to work with:
   760 (stream) + 2 x 20 (gaps) + 2 x 300 = 1400. Below it the three-column
   form would either squeeze the panel to an unreadable width or, if the
   stream were allowed to shrink instead, break the one size that must never
   change. So it stacks instead. */
@media (min-width: 1400px) {
  .page-grid {
    /* 370 + 20 + 760 + 20 + 370. Columns 1 and 3 are the same track
       definition; that identity is the centring. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 760px) minmax(0, 1fr);
    max-width: 1540px;
  }
  .page-grid-mirror { display: block; }
}

/* ── The fold is SIDEWAYS (owner, 2026-09-02) ──────────────────────────
   It used to close like every other section — the body hid, the card lost
   its height, and the panel folded UPWARD into a title bar. Measured on the
   deployed page before the change: deployed 370x796.8 px, collapsed
   370x73.6 px — the width did not move at all (x1.000) and the height fell
   to a tenth (x0.092), leaving a card wider than it was tall
   (evidence/P10/before/before_page.json). The owner asked for the opposite:
   the panel narrows to a SPINE and closes to the left.

   WHAT THIS RULE IS ALLOWED TO TOUCH, AND WHY IT IS ONLY THE CARD.

   The grid template above is NOT in this fold. Both side tracks keep the one
   `minmax(0, 1fr)` declaration they share, in every state, so they stay equal
   BY DECLARATION and the merged stream cell's centre stays the page's centre
   — nothing here measures the panel or feeds its width back into a track.
   What narrows is the CARD inside column 3, and column 3's width is set by
   the grid template, not by its content: `minmax(0, 1fr)`'s zero minimum
   means the card cannot widen its track, and a card narrower than its track
   simply leaves the rest of the track empty. So the stream keeps its exact
   rendered size, the section columns keep their positions and widths, and
   the fold is invisible to all of them — the same structural argument the
   grid comment above makes, used a second time.

   (Narrowing the TRACK instead would also have been possible — shrinking the
   grid's own max-width pulls both 1fr tracks in together and stays centred by
   symmetry — but it moves the empty balancing column too, for a difference
   nobody can see: both side tracks are empty. The card is the smaller change,
   and it leaves the page's frame identical in every state.)

   The transition and the clipping live on the folded state alone, on purpose.
   `overflow: hidden` on a deployed card is exactly the mechanism that once
   painted the body outside the box allowed to paint it ("it looks empty",
   1 px of 502), so the deployed card never carries it. The consequence is
   that closing animates — the right edge sweeps left — and opening snaps. */
#instr-panel.instr-folded {
  /* A spine: 3rem wide, and as tall as the column it lives in (16rem where
     that column has no definite height, e.g. the local stub page). Taller
     than it is wide is the measurable difference between folding sideways
     and folding upward, and the acceptance gate reads exactly that. */
  width: 3rem;
  min-width: 3rem;
  height: 100%;
  min-height: 16rem;
  margin-bottom: 0;
  /* `.card`'s 1.25rem of horizontal padding would leave 2 px of content box
     inside a 3rem card — not enough for the upright title to paint in. */
  padding: 1.25rem 0.75rem;
  overflow: hidden;
  transition: width 0.28s ease;
}
/* The title runs DOWN the spine, still starting at its top-left corner. The
   deployed card is untouched by this: there the title stays the ordinary
   horizontal one every other section has, which is how the owner asked for
   it after seeing it centred and rotated. */
#instr-panel.instr-folded .section-toggle {
  writing-mode: vertical-rl;
  width: auto;
  margin-bottom: 0;
  white-space: nowrap;
  justify-content: flex-start;
}
/* The arrow is a direction, not a letter: keep it upright so it still points
   where the edge will travel (left to close, right to open) instead of being
   rotated a quarter turn with the text. */
#instr-panel.instr-folded .toggle-icon { writing-mode: horizontal-tb; }

/* ── Blocks ────────────────────────────────────────────────────────────── */
.instr-block { margin-bottom: 1rem; }
.instr-block:last-child { margin-bottom: 0; }
.instr-block-title {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: #64748b; margin-bottom: 0.5rem;
}
.instr-block-title .hint { display: inline; margin: 0 0 0 0.4rem; text-transform: none; letter-spacing: normal; }

/* CPU bars — scaled 0-400% per the card, never 0-4 cores. */
.instr-cpu-row {
  display: flex; align-items: center; gap: 0.5rem;
  margin-bottom: 0.4rem; font-size: 0.75rem;
}
.instr-cpu-label { flex: 0 0 6.4rem; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.instr-cpu-bar {
  flex: 1 1 auto; min-width: 0;   /* a column, not a full page, is all it gets */
  height: 8px; background: #151821; border: 1px solid #2d3748;
  border-radius: 4px; overflow: hidden;
}
.instr-cpu-bar-fill {
  height: 100%; width: 0%;
  background: #38bdf8; border-radius: 3px;
  transition: width 0.4s linear;
}
.instr-cpu-bar-fill.self { background: #a78bfa; } /* the panel's own row, visually distinct */
.instr-cpu-bar-fill.unreadable { background: repeating-linear-gradient(45deg, #374151, #374151 4px, #1f2937 4px, #1f2937 8px); }
.instr-cpu-val { flex: 0 0 3.4rem; text-align: right; color: #e2e8f0; font-variant-numeric: tabular-nums; }
.instr-cpu-val.unreadable { color: #d97706; font-style: italic; }

/* Health / device state rows reuse .status-row from style.css.

   THE PANEL NEVER ASKS ITS COLUMN FOR MORE THAN A COLUMN. Its track is now
   `minmax(0, 1fr)`, whose zero minimum cannot be widened by content at all,
   so this is belt and braces rather than the load-bearing fix it was when the
   panel sat in `.two-col` (`1fr` = `minmax(auto, 1fr)`, which never shrinks
   below min-content, so a card holding one unbreakable string took that width
   out of the OTHER column). Keep it anyway: without it the content OVERFLOWS
   its track instead of widening it, which pushes the document wider than the
   viewport — the same defect, sideways. It is not hypothetical: `Live
   throttle` reads back a whole sysfs path when it cannot be read ("n/a:
   sysfs: /sys/devices/platform/soc/soc:firmware/..."), with nothing in it a
   line break is allowed to land on, and it alone drove the deployed panel's
   min-content to 425 px against a 370 px column, shifting two left-column
   cards 15 px (measured: evidence/P8/after/panel_minwidth.json). A value here
   is an arbitrary failure reason, so it wraps anywhere rather than being
   trusted to be short. */
.instr-val-unreadable { color: #d97706; font-style: italic; }
#instr-panel .status-row { gap: 0.75rem; align-items: baseline; }
#instr-panel .status-row > :first-child { flex: 0 0 auto; }
#instr-panel .status-val {
  flex: 0 1 auto; min-width: 0; text-align: right;
  overflow-wrap: anywhere;   /* not `break-word`: only `anywhere` shrinks the
                                min-content size, which is the whole point */
}

/* Event history — newest first, capped, and scrolling INSIDE the card: the
   list is the one part of this panel with no natural bound, so it carries its
   own. */
.instr-events {
  max-height: 16rem; overflow-y: auto;
  border: 1px solid #2d3748; border-radius: 8px;
  background: #151821;
}
.instr-event {
  display: flex; gap: 0.5rem; padding: 0.35rem 0.55rem;
  font-size: 0.72rem; border-bottom: 1px solid #1e2330;
}
.instr-event:last-child { border-bottom: none; }
.instr-event-ts { flex: 0 0 4.4rem; color: #475569; font-variant-numeric: tabular-nums; }
.instr-event-kind {
  /* Shrinkable, and it wraps if a kind is ever long enough to matter: the
     set of kinds is open (H33), and an unknown one must not be able to
     widen the column — see the note on .status-val above. */
  flex: 0 1 auto; min-width: 0; padding: 0 0.35rem; border-radius: 4px;
  background: #2d3748; color: #94a3b8; font-weight: 700;
  letter-spacing: 0.03em; overflow-wrap: anywhere;
}
/* Known kinds get a bit of colour; an unrecognised kind keeps the default
   grey badge above and is still rendered in full — never dropped. */
.instr-event-kind.err     { background: #4c1d1d; color: #f87171; }
.instr-event-kind.warn    { background: #4a3510; color: #fbbf24; }
.instr-event-kind.approx  { opacity: 0.65; }
.instr-event-text { flex: 1 1 auto; min-width: 0; color: #cbd5e1; word-break: break-word; }

#instr-conn-hint { margin-top: 0.75rem; }
