/* ---- analyze: controls ---- */
.an-controls {
  padding: 0.86rem 1.43rem 1rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.an-row {
  display: flex;
  align-items: center;
  gap: 0.57rem;
  flex-wrap: wrap;
  margin-bottom: 0.57rem;
}
.an-label {
  flex: 0 0 5rem;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--muted);
}

.an-controls input {
  padding: 0.43rem 0.57rem;
  font: 0.86rem var(--sans);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  max-width: 100%;
}
#anSearch { min-width: 10rem; flex: 0 1 14rem; }
/* The primary accent button. Excludes the picker's own buttons (category chips, sensor results),
   which carry their own styling below and would otherwise be flooded solid-accent by this broader,
   higher-specificity rule. */
.an-controls button:not(.ghost):not(.an-catchip):not(.an-result):not(.an-session) {
  padding: 0.43rem 0.86rem;
  font: 600 0.86rem var(--sans);
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: 0.5rem;
  cursor: pointer;
}
.an-controls button:disabled { opacity: .45; cursor: not-allowed; }
.an-controls .ghost {
  padding: 0.43rem 0.86rem;
  font: 500 0.86rem var(--sans);
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  cursor: pointer;
}

/* ---- device categories ----
   A sensor's device kind (CPU/GPU/RAM/…), derived from its id in util/hardware-category.js. The chip
   is UI chrome, not chart data, so it carries its own categorical hues — one per class, exposed as
   --cat so the single chip rule can colour itself from whichever class is present. */
.an-cat {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 0.07rem 0.43rem;
  font: 700 0.68rem var(--sans);
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--cat, var(--muted));
  background: color-mix(in srgb, var(--cat, var(--muted)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--cat, var(--muted)) 38%, transparent);
  border-radius: 999px;
}
.cat-cpu { --cat: #4a9eff; }
.cat-gpu { --cat: #3fb950; }
.cat-ram { --cat: #a78bfa; }
.cat-storage { --cat: #e3a008; }
.cat-mobo { --cat: #2dd4bf; }
.cat-net { --cat: #8b949e; }
.cat-battery { --cat: #f472b6; }
.cat-other { --cat: #8b949e; }

/* ---- analyze: the sensor picker ----
   Category chips filter, the search box narrows, a click on a result adds it. Replaced a <select>,
   which buried one of a machine's hundreds of sensors behind a scroll. */
.an-row-picker { align-items: flex-start; }
.an-picker { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.an-picker-top { display: flex; align-items: center; gap: 0.57rem; flex-wrap: wrap; }
.an-picker-top #anSearch { flex: 1 1 12rem; max-width: 24rem; }

.an-cats { display: flex; flex-wrap: wrap; gap: 0.36rem; }
.an-catchip {
  display: inline-flex;
  align-items: center;
  gap: 0.29rem;
  padding: 0.21rem 0.57rem;
  font: 600 0.79rem var(--sans);
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.an-catchip:hover { color: var(--text); border-color: var(--muted); }
.an-catchip-count {
  padding: 0 0.29rem;
  font: 600 0.68rem var(--mono);
  color: var(--muted);
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  border-radius: 999px;
}
/* On: the chip takes its category's colour, so which filter is active reads at a glance. The All chip
   has no category class, so --cat is unset and it falls back to the accent. */
.an-catchip.is-on {
  color: var(--cat, var(--accent));
  border-color: color-mix(in srgb, var(--cat, var(--accent)) 55%, transparent);
  background: color-mix(in srgb, var(--cat, var(--accent)) 14%, transparent);
}
.an-catchip.is-on .an-catchip-count {
  color: var(--cat, var(--accent));
  background: color-mix(in srgb, var(--cat, var(--accent)) 22%, transparent);
}

/* A single scrolling column: a device heading, then that device's sensors listed one per row beneath
   it — a list, not a wrapping cloud of chips, so a device with a dozen sensors stays scannable. */
.an-results {
  display: flex;
  flex-direction: column;
  gap: 0.07rem;
  max-height: 16rem;
  overflow-y: auto;
  padding: 0.14rem;
}
.an-results.is-loading { opacity: .5; }
.an-result-group {
  display: flex;
  align-items: center;
  gap: 0.43rem;
  margin-top: 0.57rem;
  padding: 0 0.29rem 0.21rem;
  border-bottom: 1px solid var(--border);
}
.an-result-group:first-child { margin-top: 0; }
.an-results-note { padding: 0 0.29rem; }
.an-result-hw {
  min-width: 0;
  font: 600 0.79rem var(--sans);
  color: color-mix(in srgb, var(--text) 78%, transparent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* One sensor per row: name on the left, its type and unit on the right. A row that highlights on
   hover rather than a bordered chip, so the group reads as a list. */
.an-result {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.86rem;
  width: 100%;
  padding: 0.29rem 0.5rem 0.29rem 0.71rem;
  font: 500 0.82rem var(--sans);
  color: var(--text);
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 0.36rem;
  cursor: pointer;
}
.an-result:hover:not(:disabled) { color: var(--accent); background: var(--surface-2); }
.an-result:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.an-result:disabled { opacity: .45; cursor: not-allowed; }
.an-result-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
.an-result-type { flex: none; font: 0.72rem var(--mono); color: var(--muted); }

/* ---- analyze: chosen sensors ---- */
.an-chosen { display: flex; flex-wrap: wrap; gap: 0.43rem; margin-top: 0.29rem; }
.an-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.36rem;
  padding: 0.21rem 0.29rem 0.21rem 0.36rem;
  font: 600 0.86rem var(--sans);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
}
/* The device this sensor lives on, ellipsised so a long product string cannot stretch the tag. */
.an-tag-hw {
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  color: color-mix(in srgb, var(--text) 66%, transparent);
}
.an-tag-name { white-space: nowrap; font-weight: 700; }
.an-tag-x {
  padding: 0 0.29rem;
  font-size: 1rem;
  line-height: 1;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.an-tag-x:hover { color: var(--critical); }
.an-hint, .an-status { font-size: 0.86rem; color: var(--muted); }
.an-status { margin: 0.57rem 0 0; }

/* ---- analyze: loading ----
   A /series query over a long session takes tens of seconds. Text alone does not read as "working"
   over that span, so something has to move. */
.an-status[data-state="busy"] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text);
}
.an-spinner {
  flex: none;
  width: 0.86rem;
  height: 0.86rem;
  border: 2px solid var(--track);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: an-spin 0.7s linear infinite;
}
.an-busy-elapsed { font: 0.79rem var(--mono); color: var(--muted); }

@keyframes an-spin { to { transform: rotate(360deg); } }

/* The one place an animation is load-bearing rather than decorative, so it degrades to a static
   ring rather than disappearing: a still spinner still reads as a progress indicator. */
@media (prefers-reduced-motion: reduce) {
  .an-spinner { animation: none; }
  .an-skeleton-plot { animation: none; }
}

/* Existing charts stay readable while the next query runs — replacing them with placeholders would
   take away the thing being compared against. */
.an-charts.is-busy .an-card:not(.is-skeleton) { opacity: .45; transition: opacity .15s; }

.an-card.is-skeleton .an-card-head { font-weight: 650; color: var(--muted); }
.an-skeleton-plot {
  height: 15rem;
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--track) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: an-shimmer 1.4s ease infinite;
}
@keyframes an-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }


/* ---- analyze: buffered-telemetry notice ----
   Sits between the controls and the charts, aligned to the charts' own padding. It explains the gap
   at the end of a run's chart: the store this view reads is written by a periodic drain, so the most
   recent frames are queued, not lost. Populated from GET /ingest/status. */
.an-drain {
  display: flex;
  align-items: center;
  gap: 0.57rem;
  margin: 0.86rem 1.43rem 0;
  padding: 0.5rem 0.86rem;
  font-size: 0.86rem;
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: var(--radius);
}
/* The element carries the `hidden` attribute; `display: flex` above would otherwise beat it. */
.an-drain[hidden] { display: none; }
/* "No readings incoming": a settled grey rather than the accent-tinted "waiting" look, and a still
   dot, so an idle machine does not read as if something is pending. */
.an-drain.is-quiet {
  color: var(--muted);
  background: color-mix(in srgb, var(--muted) 10%, var(--surface));
  border-color: color-mix(in srgb, var(--muted) 30%, transparent);
}
.an-drain.is-quiet .an-drain-dot { background: var(--muted); animation: none; }
.an-drain-dot {
  flex: none;
  width: 0.57rem;
  height: 0.57rem;
  border-radius: 50%;
  background: var(--accent);
  /* A slow pulse says "waiting", where a spinner would say "loading" — nothing is being fetched. */
  animation: an-drain-pulse 1.6s ease-in-out infinite;
}
.an-drain-main { font-weight: 600; }
.an-drain-when { color: var(--muted); font-variant-numeric: tabular-nums; }
@keyframes an-drain-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .an-drain-dot { animation: none; }
}
@media (max-width: 860px) {
  .an-drain { margin-left: 0.86rem; margin-right: 0.86rem; flex-wrap: wrap; gap: 0.29rem 0.57rem; }
}

/* ---- analyze: charts ---- */
.an-charts {
  display: flex;
  flex-direction: column;
  gap: 0.86rem;
  padding: 1rem 1.43rem 2rem;
}
.an-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0.86rem 1rem 0.71rem;
}
.an-card-head {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem 0.86rem;
  flex-wrap: wrap;
  margin-bottom: 0.57rem;
}
/* Hardware over sensor name: the device chip and its name on top, the sensor name as the title below,
   so "which device" is as legible as "which reading". */
.an-card-ident { display: flex; flex-direction: column; gap: 0.21rem; min-width: 0; }
.an-card-hw { display: flex; align-items: center; gap: 0.43rem; min-width: 0; }
.an-card-hw-name {
  min-width: 0;
  font: 600 0.86rem var(--sans);
  color: color-mix(in srgb, var(--text) 82%, transparent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.an-card-title { font-weight: 650; font-size: 1.05rem; line-height: 1.15; }
.an-summary { margin-left: auto; display: flex; gap: 0.86rem; font: 600 0.93rem var(--mono); }
.an-stat { display: inline-flex; align-items: baseline; gap: 0.29rem; }
.an-stat-label {
  font: 500 0.72rem var(--sans);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
/* Orange and teal, matching the max/min markers drawn on the plot, so each number and its point read
   as one thing. */
.an-stat.is-max { color: var(--max); }
.an-stat.is-min { color: var(--min); }

/* Per-graph view tools: a small toggle strip under the header. Off is a quiet outline; on picks up
   the accent to say it is changing what the chart shows. See components/chart-band.js for each. */
.an-toolbar { display: flex; flex-wrap: wrap; gap: 0.36rem; margin-bottom: 0.5rem; }
.an-tool {
  font: 500 0.75rem var(--sans);
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.21rem 0.64rem;
  cursor: pointer;
}
.an-tool:hover { color: var(--text); border-color: var(--muted); }
.an-tool.is-on {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* Reset zoom: present only while a drag-zoom is active, so it reads as an accent action rather than
   another off toggle. */
.an-tool-reset {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.an-tool-reset:hover { color: var(--accent); border-color: var(--accent); }

/* Sized in JS to the wrapper's measured width, so the drawing is 1:1 with CSS pixels and the axis
   text is not stretched — see components/chart-band.js. `max-width` keeps a redraw that lags a
   shrinking window from widening the card and feeding back into the resize. */
/* Crosshair cursor to invite the drag-to-zoom — the plot is a selectable region, not just a picture. */
.an-plot { display: block; max-width: 100%; overflow: visible; cursor: crosshair; }
/* The band is the range the readings actually covered; the line is their average. Both matter, so
   the band is filled faintly rather than outlined - an outline reads as two more series. */
.an-band { fill: color-mix(in srgb, var(--accent) 22%, transparent); stroke: none; }
.an-line { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.an-grid { stroke: var(--track); stroke-width: 1; vector-effect: non-scaling-stroke; }

/* Voltage reference lines: a nominal level and its ±5% tolerance, so a rail has something to read its
   wander against. The nominal is a clearer dash; the tolerance is fainter and only appears as the rail
   nears it. Non-scaling so the dash stays crisp at any width. */
.an-ref { fill: none; vector-effect: non-scaling-stroke; }
.an-ref-nominal { stroke: color-mix(in srgb, var(--text) 45%, transparent); stroke-width: 1; stroke-dasharray: 5 4; }
.an-ref-tol { stroke: color-mix(in srgb, var(--text) 28%, transparent); stroke-width: 1; stroke-dasharray: 2 5; }
.an-ref-label { fill: var(--muted); font: 0.64rem var(--mono); }
.an-axis { fill: var(--muted); font: 0.72rem var(--mono); }
.an-axis-date { fill: var(--text); opacity: .75; }
.an-tick { stroke: var(--track); stroke-width: 1; }
.an-empty { margin: 0.29rem 0; font-size: 0.86rem; color: var(--muted); }

/* The `.views { order: 3 }` rule that used to live in this media query is shared tab chrome, not
   Analyze's, and now sits in styles.css alongside the rest of the tab strip. */
@media (max-width: 860px) {
  .an-label { flex-basis: 100%; }
  .an-controls, .an-charts { padding-left: 0.86rem; padding-right: 0.86rem; }
  .an-summary { margin-left: 0; flex-basis: 100%; }
}

/* ---- analyze: session picker ----
   A horizontal strip of runs rather than a dropdown: several can be on at once, and which ones
   are is the thing you are constantly adjusting, so it has to be visible without opening anything. */
.an-row-sessions { align-items: flex-start; }
.an-session-box { flex: 1; min-width: 0; }

.an-sessions {
  display: flex;
  gap: 0.43rem;
  overflow-x: auto;
  padding-bottom: 0.29rem;
}

.an-session {
  display: flex;
  flex-direction: column;
  gap: 0.14rem;
  flex: none;
  padding: 0.43rem 0.71rem;
  text-align: left;
  font: inherit;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  cursor: pointer;
}
.an-session:hover { filter: brightness(1.12); }
.an-session:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.an-session.is-on {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}
.an-session-when { font: 600 0.86rem var(--sans); white-space: nowrap; }
/* Mixed towards the text colour rather than left at --muted: --muted was picked to sit on --bg, and
   on the lighter --surface-2 of a session button it dropped to the point of being unreadable. */
.an-session-meta {
  font: 0.72rem var(--mono);
  color: color-mix(in srgb, var(--text) 62%, transparent);
  white-space: nowrap;
}
.an-session.is-on .an-session-meta { color: color-mix(in srgb, #fff 82%, transparent); }

/* Connected, recorded nothing. Shown because it happened, but there is nothing to graph. */
.an-session.is-empty { opacity: .5; cursor: not-allowed; }
.an-session.is-empty:hover { filter: none; }

/* The delete control overlays the card's top-right corner rather than taking a column of its own —
   these cards scroll horizontally and a trailing cell on each would widen the strip. Muted at rest,
   red on hover, because it removes the run from the cloud for good. */
.an-session-item { position: relative; flex: none; display: flex; }
.an-session-item > .an-session { padding-right: 1.9rem; }
.an-session-del {
  position: absolute;
  top: 0.29rem;
  right: 0.29rem;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  font-size: 0.79rem;
  line-height: 1;
  color: color-mix(in srgb, var(--text) 55%, transparent);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 0.35rem;
  cursor: pointer;
}
/* Lifted off the accent fill of a selected card, where the muted glyph would otherwise disappear. */
.an-session.is-on + .an-session-del { color: color-mix(in srgb, #fff 78%, transparent); }
.an-session-del:hover { color: #fff; background: #e5484d; border-color: #e5484d; }
.an-session-del:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.an-session-actions {
  display: flex;
  align-items: center;
  gap: 0.43rem;
  margin-top: 0.36rem;
}

/* ---- analyze: hover ----
   The plot is stretched to the card, so the tooltip is positioned in percentages of the viewBox
   rather than in pixels — see components/chart-band.js for the coordinate mapping. */
.an-plot-wrap { position: relative; }

/* The displayed-frame count, a small chip in the plot's top-right. Muted and click-through so it
   annotates without covering data or catching the hover crosshair. */
.an-count {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  padding: 0.06rem 0.4rem;
  font: 0.68rem var(--mono);
  color: var(--muted);
  background: color-mix(in srgb, var(--surface) 74%, transparent);
  border-radius: 0.35rem;
  pointer-events: none;
}

.an-cross { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
/* The drag-to-zoom selection band. pointer-events off so it never intercepts the drag it is drawn for. */
.an-select {
  fill: color-mix(in srgb, var(--accent) 16%, transparent);
  stroke: var(--accent);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
  pointer-events: none;
}
.an-dot {
  fill: var(--accent);
  /* A surface-coloured ring keeps the marker legible where it sits on the line. */
  stroke: var(--surface);
  stroke-width: 2;
}
/* The lowest and highest readings in view, marked with their values so the extremes are legible on
   every graph — teal for the minimum, orange for the maximum. */
.an-min-dot, .an-max-dot {
  stroke: var(--surface);
  stroke-width: 2.5;
  pointer-events: none;
}
.an-min-dot { fill: var(--min); }
.an-max-dot { fill: var(--max); }
.an-min-label, .an-max-label {
  font: 700 0.72rem var(--mono);
  /* A surface halo keeps the label readable where it sits over the line or a gridline. */
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 3px;
  stroke-linejoin: round;
  pointer-events: none;
}
.an-min-label { fill: var(--min); }
.an-max-label { fill: var(--max); }

.an-tip {
  position: absolute;
  transform: translate(0.71rem, -50%);
  display: flex;
  flex-direction: column;
  gap: 0.07rem;
  padding: 0.36rem 0.57rem;
  font: 0.79rem var(--mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--text);
  background: color-mix(in srgb, var(--surface-2) 96%, transparent);
  border: 1px solid var(--border);
  border-radius: 0.43rem;
  box-shadow: var(--shadow);
  pointer-events: none;
  z-index: 1;
}
/* Near the right edge the tooltip flips to the other side of the crosshair rather than overflowing. */
.an-tip.is-flipped { transform: translate(calc(-100% - 0.71rem), -50%); }
.an-tip-when { color: var(--muted); font-size: 0.72rem; }
.an-tip-avg { font-weight: 700; }
.an-tip-range { color: var(--muted); }
.an-tip-n { color: var(--muted); font-size: 0.72rem; }
.an-tip-session { color: var(--accent); font-size: 0.72rem; }

/* The boundary between two sessions on one plot. */
.an-split {
  stroke: var(--muted);
  stroke-width: 1;
  stroke-dasharray: 2 3;
  opacity: .7;
  vector-effect: non-scaling-stroke;
}
