/* ==========================================================================
   components.css

   Two ideas run through everything here:

   1. Sections are announced by a label and a hairline, never by a box with the
      title notched into its border. Fewer nested frames, more alignment.
   2. Anything the sensor said is set in mono with tabular figures, so digits
      hold their column and a value updating in place does not jitter.
   ========================================================================== */

/* --- section heading ---------------------------------------------------- */

/* The rule runs from the label to the right edge: it reads as a datasheet
   heading rather than a card title. */
.panel__legend,
.field-group__legend,
.sidebar__legend,
.log__toolbar > .log__status,
.scan-dialog__results-title,
.log__hints-title {
  font: 600 var(--text-2xs) / 1 var(--font-sans);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

.panel__legend {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  color: var(--accent);
}

.panel__legend::after {
  content: '';
  flex: 1;
  height: var(--hairline);
  background: var(--border);
}

/* --- panel -------------------------------------------------------------- */

.panel {
  min-width: 0;
  background: var(--surface);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5) var(--space-5);
  box-shadow: var(--shadow-panel);
}

.panel--log {
  height: 100%;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: var(--space-3) var(--space-5) var(--space-4);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.panel__body {
  min-width: 0;
}

.panel__description,
.panel__hint,
.sidebar__description,
.sidebar__hint,
.custom-panel__hint,
.address-panel__hint,
.scan-dialog__intro,
.log__hint {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
}

.panel__description {
  margin-bottom: var(--space-4);
  max-width: 68ch;
}

.panel__hint,
.custom-panel__hint,
.address-panel__hint {
  margin-top: var(--space-3);
}

.panel__empty {
  color: var(--text-faint);
  font-size: var(--text-sm);
  padding: var(--space-5) 0;
}

/* --- panel toolbar ------------------------------------------------------ */

.panel__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: var(--hairline) solid var(--border);
}

.panel__subtitle,
.sensor-panel__name {
  font: 600 var(--text-lg) / 1.2 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text);
}

/* Push the controls to the right of the sensor name. */
.sensor-panel__read-all {
  margin-left: auto;
}

/* The poll control is one unit: checkbox, label, number, unit. It must not
   let the number input stretch across the panel. */
.sensor-panel__poll-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  white-space: nowrap;
}

/* Poll switch + interval: one control, one border, so it cannot wrap apart. */
.sensor-panel__pollgroup {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
}

.sensor-panel__interval-label,
.sensor-panel__interval-unit {
  color: var(--text-muted);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.sensor-panel__pollgroup .sensor-panel__interval {
  width: 5rem;
  flex: none;
  min-height: var(--control-height-sm);
  text-align: right;
}

/* --- field group -------------------------------------------------------- */

.field-group {
  /* fieldset carries UA border/margin/padding: strip it all. */
  margin: 0 0 var(--space-5);
  padding: 0;
  border: 0;
  min-width: 0;
}

.field-group:last-child {
  margin-bottom: 0;
}

.field-group__legend {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: 0;
  margin-bottom: var(--space-2);
}

.field-group__legend::after {
  content: '';
  flex: 1;
  height: var(--hairline);
  background: var(--border);
}

/* --- field row: the aligned record -------------------------------------- */

.sensor-panel__body .field-row {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 20rem) 7.5rem 4rem minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-1) 0;
  border-bottom: var(--hairline) solid var(--border);
}

.sensor-panel__body .field-row:last-child {
  border-bottom: 0;
}

/* Surface the register number the original tool buried in a log line.
   `data-register` is already on the row, so this needs no JS.
   Scoped to the sensor panel: the sidebar, address and custom rows reuse
   .field-row and must not get a phantom first grid cell. */
.sensor-panel__body .field-row[data-register]::before {
  content: 'R' attr(data-register);
  font: 500 var(--text-2xs) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  letter-spacing: var(--tracking-mono);
  text-align: right;
  padding-right: var(--space-1);
}

/* Stub rows keep the column empty so every row in the panel still lines up. */
.sensor-panel__body .field-row:not([data-register])::before {
  content: '';
}

.field-row__action {
  justify-self: stretch;
  justify-content: flex-start;
  text-align: left;
}

.field-row__value {
  font: 500 var(--text-xl) / 1.1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-mono);
  text-align: right;
  color: var(--text);
  white-space: nowrap;
}

.field-row__value--empty,
.field-row__value--pending {
  color: var(--text-faint);
}

.field-row__value--pending {
  animation: pulse 1.1s ease-in-out infinite;
}

.field-row__value--error {
  color: var(--danger);
  font-size: var(--text-md);
}

.field-row__value--ok {
  color: var(--text);
}

@keyframes pulse {
  50% {
    opacity: 0.35;
  }
}

.field-row__unit {
  font-size: var(--text-xs);
  color: var(--text-muted);
  white-space: nowrap;
}

.field-row__time {
  font: var(--text-2xs) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  white-space: nowrap;
}

.field-row__label {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.field-row__note {
  grid-column: 1 / -1;
  margin: 0 0 var(--space-2);
  padding-left: 3.5rem;
  color: var(--text-muted);
  font-size: var(--text-xs);
}

.field-row--disabled {
  opacity: 0.62;
}

@media (max-width: 900px) {
  .sensor-panel__body .field-row {
    grid-template-columns: 3.25rem minmax(0, 1fr) auto;
    row-gap: var(--space-1);
  }

  /* value + unit share the last column on narrow screens */
  .field-row__unit {
    grid-column: 3;
    justify-self: end;
  }

  .field-row__time {
    grid-column: 2 / -1;
    justify-self: end;
  }

  .field-row__note {
    padding-left: 3.25rem;
  }
}

/* --- buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  border: var(--hairline) solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font: 500 var(--text-sm) / 1 var(--font-sans);
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.btn:hover:not(:disabled) {
  background: var(--surface-sunken);
  border-color: var(--text-faint);
}

.btn:active:not(:disabled) {
  transform: translateY(0.5px);
}

.btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-color: var(--accent);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
}

.btn--primary:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-muted);
}

.btn--ghost:hover:not(:disabled) {
  background: var(--surface-sunken);
  border-color: var(--border);
  color: var(--text);
}

.btn svg {
  width: 1em;
  height: 1em;
  flex: none;
}

.icon {
  width: 1em;
  height: 1em;
  flex: none;
}

.status__text {
  min-width: 0;
}

/* --- inputs and selects ------------------------------------------------- */

.input,
.select,
.scan-dialog__input {
  width: 100%;
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: var(--hairline) solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font: var(--text-sm) / 1 var(--font-sans);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

/* Everything numeric is mono with tabular figures — it is instrument data. */
.input--number,
.custom-panel__register,
.custom-panel__count,
.sensor-panel__interval,
.address-panel__input,
.sidebar__address,
.scan-dialog__input {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-mono);
}

.input:hover,
.select:hover,
.scan-dialog__input:hover {
  border-color: var(--text-faint);
}

.input:focus,
.select:focus,
.scan-dialog__input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

.input:disabled,
.select:disabled {
  background: var(--surface-sunken);
  color: var(--text-faint);
  cursor: not-allowed;
}

/* Native select needs its own arrow once the UA appearance is dropped. */
.select {
  appearance: none;
  padding-right: var(--space-6);
  background-image: linear-gradient(45deg, transparent 50%, currentcolor 50%),
    linear-gradient(135deg, currentcolor 50%, transparent 50%);
  background-position: right var(--space-4) center, right calc(var(--space-4) - 4px) center;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
  cursor: pointer;
}

/* Number inputs: keep the spinners off so the mono column stays clean. */
.input--number::-webkit-outer-spin-button,
.input--number::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}

.input--number {
  appearance: textfield;
}

/* --- toggle ------------------------------------------------------------- */

.toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  user-select: none;
}

.toggle__input {
  /* A real checkbox, just sized to a proper target. */
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  flex: none;
  accent-color: var(--accent);
  cursor: pointer;
}

.toggle__input:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

.toggle__text {
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* --- badge -------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem var(--space-2);
  border: var(--hairline) solid var(--border);
  border-radius: 999px;
  background: var(--surface-sunken);
  color: var(--text-muted);
  font: 600 var(--text-2xs) / 1.6 var(--font-sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* `display` on a class outranks the UA rule for [hidden]; without this the
   badge shows on every sensor, experimental or not. */
.badge[hidden] {
  display: none;
}

.badge--experimental {
  background: var(--warn-soft);
  border-color: var(--warn-border);
  color: var(--warn);
}

/* --- status chip -------------------------------------------------------- */

.status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.status__dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 999px;
  background: var(--text-faint);
}

.status--connected .status__dot,
.status[data-status='connected'] .status__dot {
  background: var(--ok);
}

.status--connecting .status__dot,
.status[data-status='connecting'] .status__dot {
  background: var(--warn);
  animation: pulse 1s ease-in-out infinite;
}

.status--error .status__dot,
.status[data-status='error'] .status__dot {
  background: var(--danger);
}

/* --- link --------------------------------------------------------------- */

.link,
.sensor-panel__link {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
}

.link:hover,
.sensor-panel__link:hover {
  color: var(--accent-hover);
}

/* --- sidebar ------------------------------------------------------------ */

.sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.sidebar__group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.sidebar__legend {
  margin-bottom: var(--space-1);
}

.sidebar__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

/* Labelled rows put the label above the control so neither gets squeezed:
   "TH100 (experimental)" needs the whole rail width to stay readable. */
.sidebar__row > .field-row__label {
  flex: 0 0 100%;
}

.sidebar__row > .select,
.sidebar__row > .input {
  flex: 1 1 100%;
  min-width: 0;
}

.sidebar__connect,
.sidebar__scan,
.sidebar__protocol,
.sidebar__theme,
.sidebar__export {
  width: 100%;
}

.sidebar__description,
.sidebar__hint {
  margin: 0;
}

@media (max-width: 900px) {
  /* Controls that are not height-driven by --control-height still need a
     thumb-sized target. */
  .toggle__input {
    width: 1.375rem;
    height: 1.375rem;
  }

  .log__button,
  .chart__toggle {
    min-height: 40px;
    padding-inline: var(--space-3);
  }

  .sidebar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-4) var(--space-5);
    align-items: start;
  }
}

/* Protocol mode indicator: the one piece of state the original tool never
   showed, so you always knew which switch to press. */
.sidebar__protocol-mode {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-1);
  padding: var(--space-2) var(--space-3);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.sidebar__protocol-dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 999px;
  background: var(--text-faint);
}

.sidebar__protocol-mode[data-protocol='modbus'] {
  border-color: var(--accent-border);
  background: var(--accent-soft);
  color: var(--text);
}

.sidebar__protocol-mode[data-protocol='modbus'] .sidebar__protocol-dot {
  background: var(--accent);
}

.sidebar__protocol-mode[data-protocol='tbus'] {
  border-color: var(--ok-border);
  background: var(--ok-soft);
  color: var(--text);
}

.sidebar__protocol-mode[data-protocol='tbus'] .sidebar__protocol-dot {
  background: var(--ok);
}

/* --- address panel ------------------------------------------------------ */

.address-panel__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  border-bottom: 0;
  padding: 0;
}

.address-panel__row::before {
  content: none;
}

.address-panel__row .address-panel__input {
  width: 7rem;
  flex: none;
}

/* Sits apart from the "set new address" action: it is a lookup, not the
   destructive write next to it. */
.address-panel__scan {
  margin-left: auto;
}

.address-panel__target {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* --- custom panel ------------------------------------------------------- */

/* Labels on one line, inputs on the next. Top-aligned rather than bottom-
   aligned: the Register column carries a `0x0000` hint under its input, and
   bottom alignment lifted that input a line above every other control. */
.custom-panel__form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3) var(--space-4);
}

/* The hex echo gets its own reserved line so it never reflows the row. */
.custom-panel__register-hex {
  display: block;
  min-height: 1rem;
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--text-faint);
}

/* The Read button has no label above it, so it needs the label's line back to
   land on the same row as the inputs. */
.custom-panel__submit {
  flex: none;
  margin-top: calc(1.25rem + var(--space-1));
}

/* Same trap as .badge[hidden]: a class that sets `display` outranks the UA
   rule for [hidden], so the Count/Value rows never hid and FC03/FC06 showed
   both inputs at once. */
.custom-panel__row[hidden] {
  display: none;
}

.custom-panel__row {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  border-bottom: 0;
  padding: 0;
  min-width: 0;
}

.custom-panel__row::before {
  content: none;
}

.custom-panel__row .field-row__label {
  font-size: var(--text-2xs);
  line-height: 1.25rem;
  min-height: 1.25rem;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-muted);
}

.custom-panel__function,
.custom-panel__codec {
  min-width: 12rem;
}

.custom-panel__form .custom-panel__register,
.custom-panel__form .custom-panel__count,
.custom-panel__form .custom-panel__value {
  width: 8rem;
}

.custom-panel__form .custom-panel__function,
.custom-panel__form .custom-panel__codec {
  width: 12rem;
}

.custom-panel__result {
  display: flex;
  flex-wrap: wrap;
  /* Start-aligned: the decoded value and the raw registers are different type
     sizes, so centring them left the two labels on different lines. */
  align-items: flex-start;
  gap: var(--space-3) var(--space-6);
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunken);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-md);
}

.custom-panel__decoded,
.custom-panel__raw,
.custom-panel__register-hex,
.custom-panel__value-hex,
.custom-panel__address {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-mono);
}

/* One shared line box, so the hero value and the raw registers sit on the same
   baseline despite the deliberate size difference. */
.custom-panel__decoded,
.custom-panel__raw {
  min-height: 1.75rem;
  line-height: 1.75rem;
}

.custom-panel__decoded {
  font-size: var(--text-xl);
  font-weight: 500;
}

.custom-panel__raw {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.custom-panel__register-hex,
.custom-panel__value-hex,
.custom-panel__address {
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.custom-panel__status {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.custom-panel__status--ok {
  color: var(--ok);
}

.custom-panel__status--error {
  color: var(--danger);
  font-weight: 500;
}

/* --- log ---------------------------------------------------------------- */

.log {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: 0;
  flex: 1;
}

.log__toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.log__actions {
  margin-left: auto;
  display: flex;
  gap: var(--space-2);
}

.log__button {
  min-height: var(--control-height-sm);
  padding: 0 var(--space-3);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  font: 500 var(--text-2xs) / 1 var(--font-sans);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}

.log__button:hover {
  background: var(--surface-sunken);
  color: var(--text);
}

.log__viewport {
  flex: 1;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  padding: var(--space-3);
  background: var(--surface-sunken);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-md);
}

.log__lines {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  /* Long hex frames scroll here rather than being clipped. */
  min-width: max-content;
}

.log__line {
  display: flex;
  align-items: baseline;
  /* An error line carries a heading and a checklist after its text; without
     wrapping they rode along on the same row, far off to the right. */
  flex-wrap: wrap;
  gap: var(--space-3);
  font: var(--text-sm) / 1.5 var(--font-mono);
  letter-spacing: var(--tracking-mono);
  color: var(--text);
  white-space: pre;
}

.log__time {
  flex: none;
  width: var(--log-time-width);
  color: var(--text-faint);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

.log__text {
  white-space: pre;
  min-width: 0;
}

.log__line--tx .log__text {
  color: var(--tx);
}

.log__line--rx .log__text {
  color: var(--rx);
}

.log__line--ok .log__text {
  color: var(--ok);
}

.log__line--error .log__text {
  color: var(--danger);
  font-weight: 500;
}

.log__line--info .log__text {
  color: var(--text-muted);
}

/* The checklist belongs to the error line above it, so it starts where that
   line's text starts (past the timestamp column) and carries a rule down its
   left edge to show it is subordinate rather than a new entry. */
.log__hints-title {
  flex: 0 0 100%;
  margin: var(--space-1) 0 0 calc(var(--log-time-width) + var(--space-3));
  color: var(--danger);
}

.log__hints {
  flex: 0 0 100%;
  margin: var(--space-1) 0 var(--space-3) calc(var(--log-time-width) + var(--space-3));
  padding: 0 0 0 var(--space-4);
  border-left: 2px solid var(--danger-border);
  color: var(--danger);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
  white-space: normal;
}

.log__hints li {
  margin: 0;
}

.log__jump {
  position: absolute;
  right: var(--space-6);
  bottom: var(--space-6);
  z-index: 5;
  min-height: var(--control-height-sm);
  padding: 0 var(--space-3);
  border: var(--hairline) solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface-raised);
  color: var(--text);
  font-size: var(--text-xs);
  cursor: pointer;
  box-shadow: var(--shadow-pop);
}

.is-hidden {
  display: none !important;
}

.log__clipboard {
  font-size: var(--text-2xs);
  color: var(--text-faint);
}

/* --- chart -------------------------------------------------------------- */

.chart {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

.chart__plot {
  width: 100%;
  overflow-x: auto;
}

.chart__svg {
  display: block;
  width: 100%;
  height: auto;
  min-height: 200px;
  overflow: visible;
}

.chart__grid-line {
  stroke: var(--border);
  stroke-width: 1;
}

.chart__grid-label,
.chart__axis-label {
  fill: var(--text-faint);
  font-family: var(--font-mono);
  font-size: 10px;
}

.chart__line {
  fill: none;
  stroke-width: 1.75;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* One modifier drives the line, the legend swatch, the readout value and the
   cursor dot, so a track is the same colour everywhere it appears. */
.chart__series--1 { --series: var(--series-1); }
.chart__series--2 { --series: var(--series-2); }
.chart__series--3 { --series: var(--series-3); }
.chart__series--4 { --series: var(--series-4); }
.chart__series--5 { --series: var(--series-5); }
.chart__series--6 { --series: var(--series-6); }

path.chart__line {
  stroke: var(--series, var(--series-1));
}

span.chart__swatch {
  background: var(--series, var(--series-1));
}

span.chart__readout-value {
  color: var(--series, var(--text));
}

circle.chart__cursor-dot {
  stroke: var(--series, var(--series-1));
}

.chart__toggle.is-off {
  opacity: 0.4;
}

.chart__cursor-line {
  stroke: var(--text-faint);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.chart__cursor-dot {
  fill: var(--surface);
  stroke-width: 2;
}

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

.chart__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border: 0;
  background: none;
  padding: var(--space-1) 0;
  cursor: pointer;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.chart__toggle[aria-pressed='false'] {
  opacity: 0.45;
}

.chart__swatch {
  width: 0.875rem;
  height: 0.1875rem;
  border-radius: 999px;
  flex: none;
}

.chart__toggle-label {
  white-space: nowrap;
}

.chart__unit {
  margin-left: var(--space-1);
}

.chart__readout-item {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  white-space: nowrap;
}

.chart__readout {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  min-height: 1.5rem;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
}

.chart__readout-name {
  color: var(--text-muted);
}

.chart__readout-value {
  color: var(--text);
  font-weight: 500;
}

.chart__readout-time,
.chart__unit {
  color: var(--text-faint);
}

.chart__empty {
  padding: var(--space-6) 0;
  text-align: center;
  color: var(--text-faint);
  font-size: var(--text-sm);
}

/* --- scan dialog -------------------------------------------------------- */

.scan-dialog {
  width: min(30rem, calc(100vw - 2rem));
  padding: 0;
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-pop);
}

.scan-dialog::backdrop {
  background: var(--scrim);
}

.scan-dialog__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5);
}

.scan-dialog__title {
  font: 600 var(--text-lg) / 1.2 var(--font-sans);
}

.scan-dialog__range {
  display: flex;
  gap: var(--space-4);
}

.scan-dialog__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1;
}

.scan-dialog__label {
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

.scan-dialog__progress-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.scan-dialog__progress {
  flex: 1;
  height: 0.375rem;
  border-radius: 999px;
  background: var(--surface-sunken);
  overflow: hidden;
  border: var(--hairline) solid var(--border);
}

.scan-dialog__progress::-webkit-progress-bar {
  background: transparent;
}

.scan-dialog__progress::-webkit-progress-value {
  background: var(--accent);
}

.scan-dialog__progress::-moz-progress-bar {
  background: var(--accent);
}

.scan-dialog__progress-text {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.scan-dialog__results {
  /* Room for roughly six rows before scrolling, with a gutter so the scrollbar
     never sits on top of a row. */
  min-height: 6rem;
  max-height: 17rem;
  overflow-y: auto;
  padding-right: var(--space-2);
  scrollbar-gutter: stable;
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
  padding: var(--space-2);
}

.scan-dialog__results-title {
  margin-bottom: var(--space-2);
}

.scan-dialog__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* The <li> is only a list slot; the <button> inside it is the actual row.
   Styling the <li> left the button with UA chrome — white in dark mode. */
.scan-dialog__item {
  display: contents;
}

.scan-dialog__result {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font: var(--text-sm) / 1 var(--font-sans);
}

.scan-dialog__result:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.scan-dialog__result:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

.scan-dialog__result-address {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

.scan-dialog__result-value {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-muted);
}

.scan-dialog__empty {
  color: var(--text-faint);
  font-size: var(--text-sm);
}

.scan-dialog__error {
  color: var(--danger);
  font-size: var(--text-sm);
}

.scan-dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

.scan-dialog__button {
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  border: var(--hairline) solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--text);
  font: 500 var(--text-sm) / 1 var(--font-sans);
  cursor: pointer;
}

.scan-dialog__button--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
}

/* --- toasts ------------------------------------------------------------- */

.toaster {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(24rem, calc(100vw - 2rem));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: var(--hairline) solid var(--border);
  border-left: 3px solid var(--text-faint);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  box-shadow: var(--shadow-pop);
  font-size: var(--text-sm);
  pointer-events: auto;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

/* toast.js toggles these; without them dismiss() waited on a transition that
   never ran. */
.toast.is-entering,
.toast.is-leaving {
  opacity: 0;
  transform: translateY(6px);
}

.toast--success {
  border-left-color: var(--ok);
}

.toast--error {
  border-left-color: var(--danger);
}

.toast--info {
  border-left-color: var(--accent);
}

.toast__icon {
  flex: none;
  margin-top: 0.15rem;
}

.toast__message {
  flex: 1;
  min-width: 0;
}

.toast__close {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-faint);
  cursor: pointer;
}

.toast__close:hover {
  background: var(--surface-sunken);
  color: var(--text);
}
