/* Actuator measurement viewer — light/dark, one set of tokens. */

:root {
  color-scheme: light;
  --surface: #ffffff;
  --surface-sunken: #f6f5f2;
  --surface-raised: #ffffff;
  --border: #e4e3de;
  --border-strong: #d2d1ca;
  --text: #12120f;
  --text-secondary: #4b4a45;
  --text-muted: #77756d;
  --accent: #2a78d6;
  --accent-soft: #eaf1fc;
  --shadow: 0 1px 2px rgba(18, 18, 15, .05), 0 4px 16px rgba(18, 18, 15, .05);
  --radius: 10px;
  --cat-0: #2a78d6; --cat-1: #eb6834; --cat-2: #1baf7a; --cat-3: #eda100;
  --cat-4: #e87ba4; --cat-5: #008300; --cat-6: #4a3aa7; --cat-7: #e34948;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface: #161614;
    --surface-sunken: #101010;
    --surface-raised: #1e1e1b;
    --border: #302f2b;
    --border-strong: #45443f;
    --text: #f5f4ef;
    --text-secondary: #c8c6bc;
    --text-muted: #97958c;
    --accent: #3987e5;
    --accent-soft: #1a2942;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 16px rgba(0, 0, 0, .3);
    --cat-0: #3987e5; --cat-1: #d95926; --cat-2: #199e70; --cat-3: #c98500;
    --cat-4: #d55181; --cat-5: #008300; --cat-6: #9085e9; --cat-7: #e66767;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --surface: #161614;
  --surface-sunken: #101010;
  --surface-raised: #1e1e1b;
  --border: #302f2b;
  --border-strong: #45443f;
  --text: #f5f4ef;
  --text-secondary: #c8c6bc;
  --text-muted: #97958c;
  --accent: #3987e5;
  --accent-soft: #1a2942;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 16px rgba(0, 0, 0, .3);
  --cat-0: #3987e5; --cat-1: #d95926; --cat-2: #199e70; --cat-3: #c98500;
  --cat-4: #d55181; --cat-5: #008300; --cat-6: #9085e9; --cat-7: #e66767;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text);
  background: var(--surface-sunken);
  -webkit-font-smoothing: antialiased;
}

/* ---- top bar ---- */

/* Grid, not flex: a middle child needs the two outer columns to be equal
   (1fr each) to land exactly centered regardless of how wide the brand vs.
   the theme button are (TDAN-110's "logo just in the center"). */
/* Fixed #101010, regardless of theme (TDAN-127; it went white, light gray and
   briefly dark gray under TDAN-118). Colors below are hardcoded rather than
   reading the (themeable) custom properties, on purpose, so this can't drift
   with the page theme. The logos' black parts have to flip for it: ligeti is
   pure black on transparent, so `filter: invert(1)` on it is exactly white;
   IMEK can't take that (its cyan would invert to orange), so it uses a
   dark-background variant of its SVG with only the near-blacks swapped —
   see base.html and AGENTS.md. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  height: 96px;            /* fits the 84px ligeti logo; .credits' top must match */
  padding: 0 20px;
  background: #101010;
  border-bottom: 1px solid #2a2a2a;
  color: #f0efe9;
}

.topbar-left { display: flex; align-items: center; gap: 12px; justify-self: start; }

.topbar-actions { display: flex; gap: 8px; align-items: center; justify-self: end; }

.topbar .ghost { background: #101010; color: #b9b8b1; border-color: #3a3a3a; }
.topbar .ghost:hover { border-color: #5a5a5a; color: #f0efe9; }

/* IMEK's real lockup (mark + tagline) is much wider than TUHH or ligeti at
   the same height — capping width keeps it from overlapping the centered
   ligeti logo, at the cost of it not reaching the full 2x height increase
   the other two get. See AGENTS.md. */
.topbar-logo { height: 56px; width: auto; max-width: 340px; object-fit: contain; display: block; }
/* 1.5x the 56px the other two logos get (84px). */
.topbar-logo-center { justify-self: center; filter: invert(1); height: 84px; }

/* Attribution banner (TDAN-110): permanently visible, not part of #shell, so
   it survives every htmx swap untouched. Sticky right under the topbar
   (TDAN-120) so it stays on screen while scrolling instead of scrolling away
   with the page content; `top` must track .topbar's actual height. */
.credits {
  position: sticky;
  top: 96px;
  z-index: 19;
  margin: 0;
  padding: 6px 20px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-muted);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.credits-doi { font-family: ui-monospace, "SF Mono", Consolas, monospace; }

.ghost {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 6px;
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text-secondary);
  font: inherit;
  font-size: 13px;
  text-decoration: none;
  cursor: pointer;
}

.ghost:hover { border-color: var(--border-strong); color: var(--text); }

/* ---- layout ---- */

/* Sticky right below the topbar and credits banner, so it stays on screen
   while scrolling instead of scrolling away with the page content (TDAN-123
   — it should stay pinned "just like the header and the authors"). `top`
   can't be a fixed number: the credits banner's text wraps differently per
   viewport width, so its height (and therefore how far down the nav needs
   to stick) isn't constant. `--header-height`, set by app.js's
   `positionHeader`, is the real, measured topbar+credits height; the
   fallback is only for the instant before that JS runs. */
.mainnav {
  position: sticky;
  top: var(--header-height, 133px);
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 32px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.mainnav-item {
  padding: 13px 4px;
  margin-right: 18px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 14px;
  white-space: nowrap;
  cursor: pointer;
}

.mainnav-item:hover { color: var(--text); }

.mainnav-item.active {
  color: var(--text);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

.view { display: block; padding: 28px 32px 64px; min-width: 0; }
.page { max-width: 1180px; margin: 0 auto; }

select.wide { min-width: 320px; }

/* ---- headings ---- */

.crumbs {
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 6px;
}

h1 {
  margin: 0 0 6px;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -.02em;
  display: flex;
  align-items: center;
  gap: 10px;
}

h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
}

.pill {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 999px;
  padding: 3px 9px;
}

.lede, .sub {
  margin: 0;
  color: var(--text-muted);
  font-size: 13px;
  max-width: 76ch;
}

.page-head {
  margin-bottom: 24px;
}
.page-title { min-width: 0; }
/* Rendered inside .mainnav now, not .page-head (TDAN-125), so it stays
   visible while scrolling instead of scrolling away with the page. Pushed
   to the row's far end regardless of which flex container it lands in. */
.page-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-left: auto;
}

/* ---- tabs ---- */

.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.tab {
  padding: 9px 12px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
}

.tab:hover { color: var(--text); }
.tab.active { color: var(--text); border-bottom-color: var(--accent); font-weight: 550; }

/* ---- controls ---- */

.panel-controls { margin-bottom: 18px; }

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 24px;
  align-items: flex-end;
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.control { display: flex; flex-direction: column; gap: 6px; }

.control > span {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

select {
  height: 32px;
  min-width: 150px;
  padding: 0 8px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip input, .seg input { position: absolute; opacity: 0; pointer-events: none; }

.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 11px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
  background: var(--surface);
}

.chip:has(input:checked) {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 550;
}

.segmented {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 9px;
}

.seg {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 11px;
  border-radius: 7px;
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}

.seg:has(input:checked) {
  background: var(--surface);
  color: var(--text);
  font-weight: 550;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
}

/* ---- drive-voltage slider ---- */

.slider { display: flex; align-items: center; gap: 12px; }

.slider input[type="range"] {
  appearance: none;
  width: 190px;
  height: 4px;
  border-radius: 999px;
  background: var(--border-strong);
  outline: none;
  cursor: pointer;
}

.slider input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
}

.slider input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--accent);
}

.slider-value {
  min-width: 52px;
  font-size: 14px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* ---- actuator dropdown ---- */

.control.grow { flex: 0 1 340px; min-width: 240px; }

input[type="search"] {
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  width: 100%;
}

.dropdown { position: relative; }

.dropdown > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
  user-select: none;
  list-style: none;
}

.dropdown > summary::-webkit-details-marker { display: none; }

/* The disclosure arrow, drawn rather than typeset so it flips with [open]. */
.dropdown > summary::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 1.5px solid var(--text-muted);
  border-bottom: 1.5px solid var(--text-muted);
  transform: translateY(-2px) rotate(45deg);
}

.dropdown[open] > summary::after { transform: translateY(2px) rotate(225deg); }
.dropdown[open] > summary { border-color: var(--accent); color: var(--accent); }

.dropdown-panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 6px);
  left: 0;
  width: max(100%, 320px);
  max-width: 90vw;
  padding: 10px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: 0 12px 28px rgba(0, 0, 0, .18);
}

.dropdown-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.dropdown-tools input[type="search"] { flex: 1 1 auto; }

.dropdown-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 2px 12px;
  max-height: 260px;
  overflow-y: auto;
}

.dropdown-note {
  margin: 8px 2px 0;
  font-size: 12px;
  color: var(--text-muted);
}

.dropdown-empty { font-size: 12px; color: var(--text-muted); }

.pick {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--text-secondary);
  cursor: pointer;
}

.pick:hover { background: var(--surface-sunken); color: var(--text); }
.pick:has(input:disabled) { opacity: .45; cursor: not-allowed; }


.controls.inline {
  padding: 0 0 12px;
  border: none;
  background: transparent;
  border-radius: 0;
}

/* ---- cards, tiles, plots ---- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  margin-top: 22px;
}

.card-head { margin-bottom: 14px; display: flex; flex-direction: column; gap: 4px; }

.legend-tools { display: flex; gap: 8px; }
/* Add-datasheet (TDAN-69): a disclosure that becomes the link once saved.
   Add-link (IMEK-503) reuses every one of these rules: same disclosure, same
   popover-form behaviour, just a different field name and endpoint. */
.datasheet-slot, .weblink-slot { position: relative; }
/* The form is a popover so opening it cannot reflow the header. */
.datasheet-add > .datasheet-form, .weblink-add > .weblink-form {
  position: absolute;
  right: 0;
  z-index: 5;
  min-width: 320px;
  background: var(--surface);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
/* Keep .ghost's inline-flex: overriding it with inline-block dropped the
   vertical centring and the label rode high in the box (TDAN-72). Only the
   disclosure triangle needs removing. */
.datasheet-add > summary, .weblink-add > summary { cursor: pointer; list-style: none; }
.datasheet-add > summary::-webkit-details-marker,
.weblink-add > summary::-webkit-details-marker { display: none; }
.datasheet-form, .weblink-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px 12px;
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}
.datasheet-form label, .weblink-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; }
.datasheet-form input[type="url"], .weblink-form input[type="url"] { min-width: 260px; }
.datasheet-error, .weblink-error { flex: 1 0 100%; margin: 0; font-size: 12px; color: var(--text-muted); }

a.ghost.datasheet, a.ghost.weblink { text-decoration: none; white-space: nowrap; }
.photo-chip .datasheet { display: block; font-size: 11px; }

/* The webpage link shown under the actuator photo on Analysis (IMEK-503),
   alongside the top-right Add-link button — same idea as the datasheet link
   under each photo-chip in Comparison. */
.actuator-photo figcaption { margin-top: 6px; text-align: center; font-size: 11px; }

/* The datasheet PDF icon under the photo (TDAN-109): as wide as the photo
   above it, page-1 preview when the PDF is mirrored locally, a plain PDF
   glyph otherwise. */
.pdf-link {
  display: block;
  width: 100%;
  margin-top: 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-sunken);
}
.pdf-link img { display: block; width: 100%; height: auto; }
.pdf-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  aspect-ratio: 3 / 4;
  color: var(--text-muted);
}
.pdf-icon svg { width: 40%; height: auto; }
.pdf-icon span { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; }

a.ghost.download { text-decoration: none; white-space: nowrap; }

/* Portrait legend (TDAN-111): one name per row, label above its members
   rather than beside them, so the whole thing reads as a narrow column that
   floats at the screen edge instead of a wide landscape strip. */
.legend-grid { display: flex; flex-direction: column; gap: 3px; }
/* Power-class sections (TDAN-57) stay whole within one column rather than
   splitting a heading away from its members: with two columns that puts LOW
   in the first and MID+HIGH in the second, with three it is one class per
   column — both read cleanly, neither strands a label. */
.legend-group { padding: 2px 0 8px; break-inside: avoid; }
.legend-group + .legend-group { border-top: 1px solid var(--border); padding-top: 8px; }
.legend-group-label {
  display: block;
  margin-bottom: 4px;
  break-after: avoid-column;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
/* align-items:flex-start so a name that wraps to two lines sits level with
   the top of its swatch rather than dragging it to the vertical centre;
   break-inside:avoid keeps those wrapped lines in one column; overflow-wrap
   lets a long unspaced model number (VL91022-160-320H) break instead of
   widening the column or spilling over the next row. */
.legend-item {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  min-width: 0;
  font-size: 11px;
  line-height: 1.3;
  padding: 2px 3px;
  border-radius: 4px;
  user-select: none;
  break-inside: avoid-column;
  overflow-wrap: anywhere;
}
.legend-item:hover { background: var(--surface-sunken); }
.legend-item.hidden { opacity: 0.35; }
.legend-swatch { display: inline-block; width: 10px; height: 10px; margin-top: 2px; border-radius: 50%; flex-shrink: 0; }
/* Smaller than the 50px Analysis/Comparison photo chips on purpose: in a
   two-column legend that has to fit the viewport height without scrolling,
   a 30px thumbnail leaves enough of the ~140px column for the name. */
.legend-thumb {
  display: block;
  width: 30px;
  height: 30px;
  object-fit: contain;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-sunken);
}

/* Two balanced columns: the fleet (26) splits ~13/13, so the legend is a
   short block that never needs a scrollbar and never clips — rather than
   column-fill:auto, which fills column one to the full viewport height (then
   clips it at the bottom edge) before it touches column two. The parent
   card carries an explicit width so both columns have room. break-inside on
   .legend-group keeps LOW in column one and MID+HIGH in column two. */
.legend-columns {
  column-count: 2;
  column-gap: 14px;
  column-fill: balance;
}

/* The legend floats: fixed to the left screen edge, out of the layout so
   the charts keep the full page width (the earlier sticky sidebar reserved
   a flex column and squeezed them). It sits above the plots on its own
   raised surface with a shadow. The 332px width holds two columns and, on
   screens ≥1900px, fits the gutter beside the centered 1180px page without
   covering a chart.

   Vertically centered in the viewport (TDAN-121): clearing the header by a
   fixed `top` kept drifting out of sync with the header's real height (the
   credits banner's text wraps differently per viewport width) and the card
   still read as "too high" even once the two no longer overlapped. Centering
   sidesteps needing to know the header height at all. */
.overview-layout, .comparison-layout { display: block; }
.overview-main, .comparison-main { min-width: 0; }
.legend-sidebar {
  position: fixed;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  /* Below `.mainnav` (15), `.credits` (19) and `.topbar` (20), so the header
     always wins where they overlap. Being centred in the viewport, the card's
     top rises into the header once the viewport is short — zooming in on a
     wide screen does it — and at an equal z-index the legend painted over the
     tabs because it comes later in the DOM. It now slides behind them
     instead. Still above the in-flow content it must cover (popover forms
     are 5, table headers 1). */
  z-index: 10;
  width: 332px;                    /* two ~145px columns + gap + card padding */
  pointer-events: none;            /* the gap around the card stays click-through */
}
.legend-sidebar .card {
  margin-top: 0;
  padding: 14px;
  pointer-events: auto;
  /* Nothing scrolls: if two balanced columns still cannot fit (a much
     larger fleet, a very short window) the card clips rather than scrolls,
     by request. The fleet is far from that today. 16px keeps it clear of
     the viewport's top and bottom edges however tall it renders. */
  max-height: calc(100vh - 32px);
  overflow: hidden;
  background: var(--surface-raised);
  box-shadow: var(--shadow);
  border-color: var(--border-strong);
}

/* Below this width the left gutter cannot hold the 332px legend without it
   overlapping the centered 1180px page, so it goes back into the flow above
   the charts — still portrait, but balanced across up to three columns (one
   power class each) so it stays a short strip rather than one tall column. */
@media (max-width: 1900px) {
  .legend-sidebar {
    position: static; width: auto; pointer-events: auto; transform: none;
  }
  .legend-sidebar .card { padding: 18px; max-height: none; overflow: visible; }
  .legend-columns, .comparison-layout .legend-columns {
    column-count: auto;
    columns: 220px 3;
  }
}

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
  margin-bottom: 22px;
}

.tiles.compact { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

.tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
}

.tile-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.tile-value {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.tile-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

.plot {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 4px 4px;
}

.empty {
  padding: 32px;
  text-align: center;
  color: var(--text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}

/* ---- tables ---- */

.table-wrap {
  overflow: auto;
  max-height: 460px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

table { border-collapse: collapse; width: 100%; font-size: 13px; }

caption {
  text-align: left;
  padding: 12px 14px 8px;
  color: var(--text-muted);
  font-size: 12px;
}

th, td {
  padding: 8px 14px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-muted);
  z-index: 1;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--surface-sunken); }

table.numeric td { font-variant-numeric: tabular-nums; text-align: right; }
table.numeric td:first-child { text-align: left; }
table.numeric th { text-align: right; }
table.numeric th:first-child { text-align: left; }

code { font-family: var(--mono); font-size: 12px; color: var(--text-secondary); }

/* ---- loading ---- */

/* Swaps dim the outgoing content rather than replacing it with a spinner, so
   a fast response reads as an update instead of a flash of "Loading…". */
#plot-area, #panel {
  transition: opacity .18s ease;
}

#plot-area.htmx-request,
#panel.htmx-request {
  opacity: .45;
  pointer-events: none;
}

/* Plotly's own chrome, toned down to match the page. */
.js-plotly-plot .modebar {
  background: transparent !important;
}

.js-plotly-plot .modebar-btn path { fill: var(--text-muted) !important; }
.js-plotly-plot .modebar-btn:hover path { fill: var(--text) !important; }
.js-plotly-plot .modebar-group { background: transparent !important; }

@media (max-width: 900px) {
  .mainnav { padding: 0 16px; }
  .view { padding: 20px 16px 48px; }
  select.wide { min-width: 0; width: 100%; }
}

/* ---- actuator photographs ---- */
.detail-layout { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
.detail-layout .table-wrap { flex: 1 1 320px; }

.actuator-photo { margin: 0; flex: 0 0 auto; width: 200px; }
.actuator-photo img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}

.photo-strip { display: flex; flex-wrap: wrap; gap: 12px; margin: 0 0 16px; }
.photo-chip { margin: 0; width: 120px; }
.photo-chip img {
  display: block;
  width: 120px;
  height: 120px;
  object-fit: contain;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}
.photo-chip figcaption {
  margin-top: 4px;
  font-size: 11px;
  color: var(--text-muted);
  text-align: center;
  overflow-wrap: anywhere;
}
