/* ============================================================
 *  Tesserae admin styles — file map
 * ============================================================
 *
 *  This file grew from "settings page only" into a junk drawer of
 *  every ``.dx-*`` rule the admin uplift introduced. The split below
 *  is roughly the order in which selectors fire as the page renders,
 *  and the comment banners through the file mirror this map. Sections
 *  are intentionally *not* moved into separate files yet — that's a
 *  later, riskier change. For now, just navigate by these banners.
 *
 *    1. Settings page bits (legacy)
 *       - footer cards, system tab, restart modal, REST pair card
 *
 *    2. Device card v2 (.dx-device-card chrome)
 *       - per-device collapsible card on /settings/devices
 *
 *    3. Server-tab section cards (.dx-section-card + toggle pattern)
 *       - .dx-server-area, master toggle pattern, all dx-* primitives
 *
 *    4. Dark-theme overrides for the cards above
 *
 *    5. Devices-tab redesign (.dx-discovered-* + inset rows)
 *       - listening pulse, discovered rows, add-device hint band,
 *         kind defaults rows
 *
 *    6. v0.56 admin-uplift shared components
 *       - section_card chrome, pills, filter chips, inset rows,
 *         marketplace search + chips, dashboard groups, history,
 *         events (with per-type colour palette + filter chips),
 *         battery dashboard, pragmatic ``.card-head`` adapter.
 *
 *  When the file gets carved up, the natural splits are:
 *    - dx-primitives.css (sections 3 + 6's shared chrome)
 *    - dx-events.css, dx-history.css, dx-dashboards.css,
 *      dx-marketplace.css, dx-battery.css (page-specific bits)
 *    - settings.css keeps sections 1-5 (true Settings UI).
 */

/* ============================================================
 *  1. SETTINGS PAGE — legacy bits
 *     Footer, system tab, restart modal, REST pair card. These
 *     pre-date the v0.56 chrome and still use the legacy ``.card``
 *     vocabulary in places. Leave them be until the Settings page
 *     itself gets a refresh pass.
 * ============================================================ */

/* ----- Settings footer (diagnostics + session, compact) -----------
   Two compact cards laid out side by side at desktop, stacking under
   each other on phone. The cards have less padding and a smaller
   header than the configuration cards above so they read as secondary
   one-shot actions. */

.settings-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--t-space-3);
  margin-top: var(--t-space-4);
}

/* Settings → System uses this in place of the auto-fit grid: the
   cards (Updates / Backups / Authentication / Webhook) each carry
   multi-paragraph forms, so stacking reads better than the
   side-by-side grid. */
.settings-stack {
  display: flex;
  flex-direction: column;
  gap: var(--t-space-4);
}
/* Reset legacy .card { margin: 0 0 16px } so flex gap is the single
   source of truth between cards (otherwise margin + gap stack to 32px
   on the System page). Match the settings-section-list reset further
   down so all tabs share one vertical rhythm. */
.settings-stack > * { margin: 0; }

/* Updates card: channel select + check-button on one row.
   Without form--row, the .actions block right-aligns onto its own line
   and the button looks orphaned. */
.form--row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--t-space-3);
  align-items: flex-end;
}
.form--row .field--grow {
  flex: 1 1 220px;
  min-width: 0;
}
.form--row .actions--bottom {
  flex: 0 0 auto;
  margin: 0;
}

/* Recent-updates block: heading + roll-back button on the same line,
   so the button visually belongs to its section instead of floating at
   the card footer. */
.history-block {
  margin-top: var(--t-space-4);
}
.history-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--t-space-3);
  flex-wrap: wrap;
}
.history-head > h4 {
  margin: 0;
}
.history-list {
  margin: var(--t-space-2) 0 0;
  padding-left: var(--t-space-4);
}

/* Backups disclosure, collapsed by default so the System page leads
   with "create a backup" rather than a wall of old snapshots. */
.backups-disclosure {
  margin-top: var(--t-space-3);
}
.backups-disclosure > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  padding: var(--t-space-2) var(--t-space-3);
  border-radius: var(--t-radius-sm);
  user-select: none;
}
.backups-disclosure > summary::-webkit-details-marker {
  display: none;
}
.backups-disclosure > summary:hover {
  background: var(--c-surface-2, rgba(255, 255, 255, 0.04));
}
.backups-disclosure[open] > summary > .ph-caret-right {
  transform: rotate(90deg);
}
.backups-disclosure > summary > .ph-caret-right {
  transition: transform 0.12s ease;
}
.backups-disclosure > .backup-list {
  margin-top: var(--t-space-3);
}

/* Recent-updates disclosure, same pattern as backups. Wraps the
   history-list so the SHA log doesn't fill the System page by
   default. Summary names the count + flips the caret on open. */
.history-disclosure { margin: var(--t-space-2) 0 var(--t-space-3); }
.history-disclosure > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  padding: var(--t-space-1) var(--t-space-2);
  border-radius: var(--t-radius-sm);
  font-size: var(--t-size-sm);
  color: var(--t-fg-soft);
  user-select: none;
}
.history-disclosure > summary::-webkit-details-marker { display: none; }
.history-disclosure > summary:hover { background: var(--t-surface-soft); color: var(--t-fg); }
.history-disclosure[open] > summary > .ph-caret-down { transform: rotate(180deg); }
.history-disclosure > summary > .ph-caret-down { transition: transform 0.12s ease; }
.history-disclosure > .history-list { margin: var(--t-space-2) 0 0; }
.settings-actions .card--compact {
  margin: 0;
  padding: var(--t-space-4);
  display: grid;
  gap: var(--t-space-3);
}
.settings-actions .card--compact .card-head {
  margin-bottom: 0;
}
.settings-actions .card--compact .card-head h2 {
  font-size: var(--t-size-md);
}
.settings-actions .card--compact .card-head .icon-box {
  width: 28px;
  height: 28px;
  font-size: 14px;
}
.settings-actions .card--compact .card-head .lede {
  font-size: var(--t-size-sm);
  margin-top: 2px;
}
.settings-actions .card--compact .actions {
  margin: 0;
}


/* ----- Device status block ------------------------------------------ */

.device-status {
  margin: 0 0 var(--t-space-5);
  padding: var(--t-space-3) var(--t-space-4);
  background: var(--t-surface-soft);
  border-radius: var(--t-radius-sm);
  border: 1px solid var(--t-border);
}
/* Inline (in the device-card summary), just the heartbeat dot + a
   relative time. No frame, no padding, sits flush so the closed
   card stays compact. */
.device-status--summary {
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}
/* In the expanded body, keep the framed look, but give it a
   subhead-style label and sit at the top of the body so the user
   sees diagnostics immediately on expand. */
.device-status--body {
  margin: 0 0 var(--t-space-4);
}
.device-status--body .device-subhead { margin: 0 0 var(--t-space-2); }
.device-status--body .status-fields { margin: 0; }

.status-line {
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
  font-size: var(--t-size-md);
  color: var(--t-fg-soft);
}

.status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
  background: var(--t-muted);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--t-muted) 25%, transparent);
}
.status-dot.is-ok { background: var(--t-ok); box-shadow: 0 0 0 3px color-mix(in oklab, var(--t-ok) 25%, transparent); }
.status-dot.is-warn { background: var(--t-warn); box-shadow: 0 0 0 3px color-mix(in oklab, var(--t-warn) 25%, transparent); }
.status-dot.is-stale { background: var(--t-danger); box-shadow: 0 0 0 3px color-mix(in oklab, var(--t-danger) 25%, transparent); }
.status-dot.is-unknown { background: var(--t-muted); box-shadow: 0 0 0 3px color-mix(in oklab, var(--t-muted) 25%, transparent); }

.status-label { font-weight: 600; color: var(--t-fg); }

.status-fields {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--t-space-1) var(--t-space-4);
  margin: var(--t-space-3) 0 0;
  padding: 0;
  font-size: var(--t-size-sm);
}

.status-fields div { display: contents; }
.status-fields dt {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--t-muted);
}
.status-fields dd {
  margin: 0;
  color: var(--t-fg);
  font-variant-numeric: tabular-nums;
}


/* ----- Device card: collapsible -------------------------------------
   Devices crowd quickly (renderer settings + panel + quiet hours +
   timetable + footer toolbar = 5 visual blocks per card). The whole
   editable stack lives behind a <details> so the default view is the
   header + status block at-a-glance; clicking anywhere on the summary
   (or the dedicated chevron) expands the body. Open by default only
   when the user is mid-calibration (template sets ``open`` then) so
   the "which number is in the top-left?" form is visible without an
   extra click. */

.device-card-details > summary { list-style: none; cursor: pointer; }
.device-card-details > summary::-webkit-details-marker { display: none; }

.device-card-summary {
  display: flex;
  align-items: flex-start;
  gap: var(--t-space-4);
  padding: 0;
  margin: 0;
  border-radius: var(--t-radius-sm);
}
.device-card-summary-main { flex: 1 1 auto; min-width: 0; }
.device-card-summary-main .card-head { margin: 0; }
.device-card-summary-main .device-status { margin: var(--t-space-3) 0 0; }

/* Toggle pill: chevron + label on the right of the summary row.
   Label swaps "Configure" / "Hide settings" off the open state. */
.device-card-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  flex: 0 0 auto;
  padding: var(--t-space-2) var(--t-space-3);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-pill);
  background: var(--t-surface);
  color: var(--t-fg-soft);
  font-size: var(--t-size-sm);
  font-weight: 500;
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
}
.device-card-details > summary:hover .device-card-toggle,
.device-card-details > summary:focus-visible .device-card-toggle {
  border-color: var(--t-border-strong);
  color: var(--t-fg);
}
.device-card-details > summary:focus-visible {
  outline: 2px solid var(--t-accent);
  outline-offset: 2px;
}
.device-card-chevron {
  transition: transform 160ms ease;
  font-size: 16px;
}
.device-card-details[open] .device-card-chevron { transform: rotate(180deg); }

.device-card-toggle-label.when-open { display: none; }
.device-card-details[open] .device-card-toggle-label.when-open { display: inline; }
.device-card-details[open] .device-card-toggle-label.when-closed { display: none; }

.device-card-body { margin-top: var(--t-space-4); }
.device-card-body > :first-child { margin-top: 0; }

@media (max-width: 540px) {
  .device-card-summary { flex-direction: column; }
  .device-card-toggle { align-self: flex-start; }
}

/* ----- Device card: grouped sections -------------------------------
   The device card stacks distinct blocks, heartbeat status, an
   editable "Panel & orientation" group, and a footer toolbar of
   secondary/destructive actions. Divider lines + a small section
   label keep the groups legible instead of one long form. */

.device-subsection {
  /* Bumped from space-4 to space-6 so the divider has real breathing
   * room between sections, previously the slider value badge / last
   * input sat right against the next section's caps-lock subhead. */
  margin-top: var(--t-space-6);
  padding-top: var(--t-space-6);
  border-top: 1px solid var(--t-border);
}
/* Each subsection's first field also needs space below the subhead
 * label, the cascade isn't enough since some fields are macros that
 * own their top margin. */
.device-subsection > .field,
.device-subsection > .field-grid,
.device-subsection > .form-group,
.device-subsection > details,
.device-subsection > .actions {
  margin-top: var(--t-space-3);
}
/* Vertical rhythm between sibling fields within a subsection -
 * sliders especially were colliding with the next field's label. */
.device-subsection .field + .field,
.device-subsection .field-grid + .field,
.device-subsection .field + .field-grid {
  margin-top: var(--t-space-3);
}

.device-subhead {
  margin: 0;
  font-size: var(--t-size-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--t-muted);
}
/* When this heading isn't the first element of its section it stands
   for a new sub-section, give it room from whatever came above so
   the heading actually reads as a break rather than crowding the
   preceding paragraph. */
* + .device-subhead { margin-top: var(--t-space-5); }

/* Curated device-icon picker, a row of selectable glyph tiles. */
.device-icon-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--t-space-2);
}
.device-icon-pick {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-sm);
  background: var(--t-surface);
  color: var(--t-fg-soft);
  font-size: 20px;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
}
.device-icon-pick input { position: absolute; opacity: 0; pointer-events: none; }
.device-icon-pick:hover { border-color: var(--t-border-strong); color: var(--t-fg); }
.device-icon-pick:has(input:checked) {
  border-color: var(--t-accent);
  background: var(--t-accent-soft);
  color: var(--t-accent-ink);
}
.device-icon-pick:focus-within {
  outline: 2px solid var(--t-accent);
  outline-offset: 2px;
}

/* Calibrate (left) + Delete (right) on one row, divided from the
   sections above. Wraps to a column on narrow screens. */
.device-footer {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--t-space-3);
  flex-wrap: wrap;
  margin-top: var(--t-space-4);
  padding-top: var(--t-space-4);
  border-top: 1px solid var(--t-border);
}
.device-footer .calibrate-block,
.device-footer .form--inline-delete { margin: 0; }
/* Calibrate grows to fill the row; its button sits left (the shared
   .actions rule right-aligns by default). Delete stays hard right. */
.device-footer .calibrate-block { flex: 1 1 auto; }
.device-footer .calibrate-block .actions { justify-content: flex-start; }
.device-footer .form--inline-delete { flex: 0 0 auto; }
.device-footer .actions { margin: 0; }

@media (max-width: 540px) {
  .device-footer { flex-direction: column; align-items: stretch; }
  .device-footer .form--inline-delete .actions { justify-content: flex-start; }
}

/* ----- System area: backup rows ------------------------------------- */

.backup-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--t-space-2);
}

.backup-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--t-space-4);
}

/* Rotation timetable, the per-device read view of which schedules
   target this panel. Three columns at desktop (when, what, edit),
   stacking under each other on phone. The .is-disabled state dims
   the row so a turned-off schedule reads as inactive without
   removing it from the timetable. */
.timetable-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--t-space-2);
}
.timetable-row {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.4fr) auto;
  align-items: center;
  gap: var(--t-space-3);
  padding: var(--t-space-3);
  border-radius: var(--t-radius-sm);
  background: var(--c-surface-2, rgba(0, 0, 0, 0.03));
}
.timetable-row.is-disabled {
  opacity: 0.55;
}
.timetable-when {
  min-width: 0;
  font-size: var(--t-size-sm);
}
.timetable-what {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  min-width: 0;
  font-weight: 500;
}
.timetable-what .ph {
  color: var(--t-accent, currentColor);
}
.timetable-edit {
  text-align: right;
}
@media (max-width: 640px) {
  .timetable-row { grid-template-columns: 1fr; gap: var(--t-space-2); }
  .timetable-edit { text-align: left; }
}

.backup-meta {
  min-width: 0;
  display: grid;
  gap: var(--t-space-1);
}

.backup-top {
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
  flex-wrap: wrap;
  min-width: 0;
}
.backup-top code {
  font-size: var(--t-size-md);
  font-weight: 600;
}
.backup-top .pill { text-transform: lowercase; }
.backup-size {
  font-size: 12px;
  color: var(--t-muted);
}

.backup-note {
  font-size: 13px;
  color: var(--t-fg-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.backup-actions {
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
  flex-shrink: 0;
}
/* Keep the three action controls (link / button / icon-only button) at
   the same height + padding so they line up cleanly. */
.backup-actions .button,
.backup-actions button {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-1);
  padding: var(--t-space-2) var(--t-space-3);
  font-size: var(--t-size-md);
  line-height: 1.2;
}
.backup-actions form.inline { display: inline-flex; margin: 0; }

@media (max-width: 720px) {
  .backup-row { grid-template-columns: 1fr; }
  .backup-actions { justify-content: flex-end; }
}


/* Restart modal (Update + Rollback) ------------------------------ */
.restart-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: var(--t-space-6);
}
.restart-modal[hidden] { display: none; }
.restart-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  animation: lightbox-fade 120ms ease-out;
}
.restart-modal-card {
  position: relative;
  z-index: 1;
  background: var(--t-surface);
  color: var(--t-fg);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-md);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45);
  padding: var(--t-space-7) var(--t-space-7) var(--t-space-6);
  width: min(440px, 100%);
  text-align: center;
}
.restart-modal-spinner {
  font-size: 32px;
  color: var(--t-accent, var(--t-fg));
  margin-bottom: var(--t-space-3);
  display: inline-flex;
  animation: restart-spin 1s linear infinite;
}
.restart-modal-card h2 {
  margin: 0 0 var(--t-space-2);
  font-size: var(--t-size-lg);
  font-weight: 700;
  color: var(--t-fg);
}
.restart-modal-stage {
  margin: 0 0 var(--t-space-4);
  color: var(--t-fg);
  font-weight: 500;
  min-height: 1.4em;
}
.restart-modal-help {
  margin: 0;
  font-size: var(--t-size-md);
  color: var(--t-fg-soft, var(--t-fg));
  line-height: 1.45;
}
.restart-modal-error {
  margin: var(--t-space-4) 0 0;
  padding: var(--t-space-3);
  background: rgba(220, 38, 38, 0.1);
  color: var(--t-danger, #b91c1c);
  border-radius: var(--t-radius-sm);
  font-size: var(--t-size-md);
  text-align: left;
}
@keyframes restart-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* REST Pair card on Settings -> Devices. Lives next to the Add device
   card; layout mirrors the broader card system so it inherits padding /
   typography without bespoke values. */
.pair-reveal {
  margin: 0 0 var(--t-space-3) 0;
  padding: var(--t-space-3);
  border: 1px solid color-mix(in srgb, var(--t-ok, #1f8a4c) 40%, transparent);
  background: color-mix(in srgb, var(--t-ok, #1f8a4c) 8%, transparent);
  border-radius: var(--t-radius-sm);
}
.pair-reveal .pair-code {
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
  margin: var(--t-space-2) 0;
}
.pair-reveal .pair-code code {
  font-family: var(--t-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: 0.12em;
  padding: 6px 12px;
  border-radius: var(--t-radius-sm);
  background: var(--t-bg, #fff);
  border: 1px solid var(--t-fg-soft, #d0d3da);
}
.pair-codes-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.pair-codes-table th,
.pair-codes-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid color-mix(in srgb, var(--t-fg-soft, #c8ccd4) 35%, transparent);
}
.pair-codes-table th {
  font-weight: 600;
  color: var(--t-fg-soft, #6b7079);
}
.pair-codes-table code {
  font-family: var(--t-mono, ui-monospace, SFMono-Regular, monospace);
  font-weight: 600;
  letter-spacing: 0.08em;
}


/* =====================================================================
   Device card v2 + Server-tab section cards (2026-06 handoff redesign).
   Scoped under .dx-* so the legacy .device-card-* + .card styles still
   apply to any view that hasn't migrated. Tokens follow the handoff
   exactly; the global --t-* set remains the source of truth elsewhere.
   ===================================================================== */

.dx-device-card {
  --dx-teal: #15846a;
  --dx-teal-hover: #127059;
  --dx-teal-hover-2: #179476;
  --dx-teal-soft: #e7f2ee;
  --dx-ink: #2b2b29;
  --dx-ink-soft: #56564f;
  --dx-muted: #8a8a82;
  --dx-faint: #9a9a93;
  --dx-faint-2: #a3a39b;
  --dx-surface: #ffffff;
  --dx-inset: #faf9f6;
  --dx-footer-bg: #fbfaf7;
  --dx-border: #e6e5e0;
  --dx-border-soft: #efeee9;
  --dx-border-faint: #f1f0eb;
  --dx-input-border: #dedcd6;
  --dx-warn: #e0902f;
  --dx-warn-text: #b9741f;
  --dx-warn-bg: #f6ead0;
  --dx-warn-band-bg: #fbf8f1;
  --dx-warn-band-border: #efe8d6;
  --dx-warn-meter-track: #ece4cf;
  --dx-ok: #2e9e6e;
  --dx-danger: #c0392b;
  --dx-danger-border: #ecc9c4;
  --dx-danger-hover: #fbeeec;
  --dx-save-bar: #23231f;

  padding: 0;
  background: var(--t-surface);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-md);
  box-shadow: var(--t-shadow-sm), var(--t-shadow-inset);
  /* v0.69.12: ``overflow: clip`` instead of ``hidden`` so descendant
     sticky elements (the save bars) can use the viewport as their
     scroll container. ``hidden`` created a new scroll container
     scoped to the card, which meant ``position: sticky; bottom: 20px``
     stuck the bar inside the card rather than to the viewport
     bottom. ``clip`` still hides overflow past the card's rounded
     corners, just without turning the card into a scrollport.
     Supported in Chromium 90+, Firefox 81+, Safari 15.4+. */
  overflow: clip;
  margin: 0 0 var(--t-space-4);
  transition:
    border-color var(--t-trans-fast),
    box-shadow var(--t-trans);
}
.dx-device-card:hover {
  box-shadow: var(--t-shadow), var(--t-shadow-inset);
  border-color: color-mix(in oklab, var(--t-border-strong) 80%, var(--t-border));
}

/* Dark mode: the device card carries its own ``--dx-*`` palette (119 uses
   across this page), all authored as light hex. Remap the whole palette to
   the shared ``--t-*`` dark tokens in one place so every card element flips
   together, instead of patching individual selectors. */
:root[data-theme="dark"] .dx-device-card {
  --dx-teal: var(--t-accent);
  --dx-teal-hover: var(--t-accent-hover);
  --dx-teal-hover-2: var(--t-accent-hover);
  --dx-teal-soft: var(--t-accent-soft);
  --dx-ink: var(--t-fg);
  --dx-ink-soft: var(--t-fg-soft);
  --dx-muted: var(--t-muted);
  --dx-faint: var(--t-muted);
  --dx-faint-2: var(--t-muted);
  --dx-surface: var(--t-surface);
  --dx-inset: var(--t-surface-sunk);
  --dx-footer-bg: var(--t-surface-soft);
  --dx-border: var(--t-border);
  --dx-border-soft: var(--t-border);
  --dx-border-faint: color-mix(in oklab, var(--t-border) 60%, transparent);
  --dx-input-border: var(--t-border-strong);
  --dx-warn: var(--t-warn);
  --dx-warn-text: var(--t-warn);
  --dx-warn-bg: var(--t-warn-bg);
  --dx-warn-band-bg: color-mix(in oklab, var(--t-warn) 12%, var(--t-surface));
  --dx-warn-band-border: color-mix(in oklab, var(--t-warn) 30%, transparent);
  --dx-warn-meter-track: color-mix(in oklab, var(--t-warn) 22%, transparent);
  --dx-ok: var(--t-ok);
  --dx-danger: var(--t-danger);
  --dx-danger-border: color-mix(in oklab, var(--t-danger) 32%, transparent);
  --dx-danger-hover: var(--t-danger-bg);
  --dx-save-bar: #262b33;
}

/* HEADER STRIP --------------------------------------------------- */
.dx-device-card .dx-card-header {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px 22px;
  border-bottom: 1px solid var(--dx-border-soft);
}
.dx-device-card .dx-header-icon {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: var(--dx-teal-soft);
  color: var(--dx-teal);
  font-size: 19px;
}
.dx-device-card .dx-header-title { flex: 1 1 auto; min-width: 0; }
.dx-device-card .dx-title {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 16px;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--dx-ink);
}
.dx-device-card .dx-title-name { min-width: 0; }
.dx-device-card .dx-transport-badge {
  font-size: 11px;
  font-weight: 600;
  color: var(--dx-faint);
  background: var(--dx-border-faint);
  border: 1px solid var(--dx-border);
  padding: 2px 7px;
  border-radius: 5px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.dx-device-card .dx-status-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  font-size: 12.5px;
  color: var(--t-muted);
}
.dx-device-card .dx-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--dx-muted);
}
.dx-device-card .dx-status-dot.is-ok { background: var(--dx-ok); }
.dx-device-card .dx-status-dot.is-warn { background: var(--dx-warn); animation: dx-pulse 1.8s ease-in-out infinite; }
.dx-device-card .dx-status-dot.is-stale { background: var(--dx-danger); }
.dx-device-card .dx-status-dot.is-unknown { background: var(--dx-muted); }
@keyframes dx-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
.dx-device-card .dx-status-headline { font-weight: 600; }
.dx-device-card .dx-status-muted { color: var(--t-muted); }
.dx-device-card .dx-tone-ok { color: var(--dx-ok); }
.dx-device-card .dx-tone-warn { color: var(--dx-warn-text); }
.dx-device-card .dx-tone-muted { color: var(--dx-muted); }
.dx-device-card .dx-collapse-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 7px;
  border: 1px solid var(--t-border);
  background: var(--dx-surface);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--dx-ink-soft);
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.dx-device-card .dx-collapse-btn:hover {
  border-color: var(--t-border-strong);
  background: var(--t-inset);
}
.dx-device-card .dx-collapse-btn .ph-caret-down { transition: transform 0.18s ease; font-size: 14px; }
.dx-device-card[data-collapsed="true"] .dx-collapse-btn .ph-caret-down { transform: rotate(-90deg); }
.dx-device-card .dx-collapse-btn .when-closed { display: none; }
.dx-device-card[data-collapsed="true"] .dx-collapse-btn .when-open { display: none; }
.dx-device-card[data-collapsed="true"] .dx-collapse-btn .when-closed { display: inline; }
.dx-device-card[data-collapsed="true"] .dx-card-body { display: none; }
.dx-device-card[data-collapsed="true"] .dx-card-header { border-bottom-color: transparent; }

/* CONNECTION DETAILS ----------------------------------------------- */
.dx-conn-details { border-bottom: 1px solid var(--dx-border-soft); }
.dx-conn-details > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 11px 22px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dx-faint);
}
.dx-conn-details > summary::-webkit-details-marker { display: none; }
.dx-conn-details > summary:hover { color: var(--dx-ink-soft); }
.dx-conn-details > summary .ph-caret-right {
  transition: transform 120ms ease;
  font-size: 13px;
}
.dx-conn-details[open] > summary .ph-caret-right { transform: rotate(90deg); }
.dx-conn-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 18px;
  padding: 0 22px 16px 42px;
  margin: 0;
  font-size: 13px;
}
.dx-conn-grid > div { display: contents; }
.dx-conn-grid dt {
  color: var(--dx-faint);
  font-weight: 600;
}
.dx-conn-grid dd {
  margin: 0;
  color: var(--dx-ink);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-family: ui-monospace, Menlo, monospace;
  font-size: 12.5px;
}
.dx-conn-grid dd code {
  background: transparent;
  padding: 0;
  font-family: inherit;
}
.dx-conn-grid dd .dx-conn-plain {
  font-family: inherit;
}
.dx-conn-grid .form--inline { display: inline-flex; margin: 0; }
.dx-reveal-btn {
  background: none;
  border: 0;
  padding: 0 2px;
  font: inherit;
  font-family: var(--t-font);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--dx-teal);
  cursor: pointer;
}
.dx-reveal-btn:hover { color: var(--dx-teal-hover); text-decoration: underline; }
.dx-transport-current { font-weight: 600; }

/* TAB BAR --------------------------------------------------------- */
.dx-tabs {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 0 14px;
  border-bottom: 1px solid var(--dx-border);
  /* Mobile: the four tabs overflow a narrow viewport, so let them
     scroll horizontally rather than wrap or clip. Snap so each tap
     lands cleanly. -webkit-overflow-scrolling for older iOS Safari. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}
.dx-tabs::-webkit-scrollbar { display: none; }
.dx-tab {
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 12px 15px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--t-muted);
  cursor: pointer;
  transition: color 120ms ease;
  flex: 0 0 auto;
  scroll-snap-align: start;
  white-space: nowrap;
}
.dx-tab:hover,
.dx-tab:focus,
.dx-tab:focus-visible {
  background: transparent;
  border: 0;
  box-shadow: none;
  color: var(--dx-ink-soft);
  outline: none;
}
.dx-tab.is-active { color: var(--dx-teal); }

/* TAB PANELS ------------------------------------------------------ */
.dx-panel {
  display: none;
  padding: 22px;
}
.dx-panel.is-active { display: block; }

/* STATUS TAB ------------------------------------------------------ */
.dx-stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.dx-stat-grid--device {
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
}
.dx-stat-tile {
  background: var(--dx-inset);
  border: 1px solid var(--t-border);
  border-radius: 8px;
  padding: 13px 14px;
}
.dx-stat-label {
  text-transform: uppercase;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--dx-faint);
}
.dx-stat-value {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 650;
  color: var(--dx-ink);
  margin-top: 4px;
}
.dx-stat-sub {
  font-size: 11px;
  color: var(--dx-faint-2);
  margin-top: 4px;
  font-family: ui-monospace, Menlo, monospace;
}
.dx-stat-sub code {
  background: transparent;
  padding: 0;
  font-size: inherit;
  font-family: inherit;
}
.dx-bars {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 14px;
}
.dx-bar {
  width: 3px;
  background: var(--t-border-strong);
  border-radius: 1px;
}
.dx-bar:nth-child(1) { height: 35%; }
.dx-bar:nth-child(2) { height: 55%; }
.dx-bar:nth-child(3) { height: 75%; }
.dx-bar:nth-child(4) { height: 100%; }
.dx-bar.is-on { background: var(--dx-ok); }
.dx-muted { color: var(--dx-faint-2); font-weight: 500; }

/* SMART SYNC ------------------------------------------------------ */
.dx-smart-sync {
  border: 1px solid var(--t-border);
  border-radius: 8px;
  margin-top: 14px;
  overflow: hidden;
}
.dx-smart-sync-head {
  background: var(--dx-warn-band-bg);
  border-bottom: 1px solid var(--dx-warn-band-border);
  padding: 13px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.dx-smart-sync-head-left {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
}
.dx-smart-sync-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dx-ink-soft);
}
.dx-smart-sync-pill {
  font-size: 11px;
  font-weight: 600;
  color: var(--dx-warn-text);
  background: var(--dx-warn-bg);
  padding: 3px 10px;
  border-radius: 20px;
}
.dx-smart-sync-pill.dx-tone-ok { color: var(--dx-ok); background: #e2f4ec; }
.dx-smart-sync-pill.dx-tone-muted { color: var(--dx-muted); background: var(--dx-border-faint); }
.dx-smart-sync-confidence {
  font-size: 11px;
  color: var(--dx-ink-soft);
}
.dx-smart-sync-meter {
  height: 5px;
  background: var(--dx-warn-meter-track);
  margin: 0;
}
.dx-smart-sync-meter-fill {
  height: 100%;
  background: var(--dx-warn);
  transition: width 220ms ease;
}
.dx-smart-sync-meter-fill.dx-tone-ok { background: var(--dx-ok); }
.dx-smart-sync-meter-fill.dx-tone-muted { background: var(--dx-faint-2); }
.dx-smart-sync-explainer {
  padding: 12px 16px 0;
  margin: 0;
  font-size: 12px;
  color: var(--t-muted);
}
.dx-smart-sync-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin: 12px 0 0;
  padding: 0;
}
.dx-smart-sync-grid > div {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-top: 1px solid var(--dx-border-faint);
}
.dx-smart-sync-grid > div:nth-child(2n) { border-left: 1px solid var(--dx-border-faint); }
.dx-smart-sync-grid dt {
  font-size: 12.5px;
  color: var(--dx-muted);
  margin: 0;
}
.dx-smart-sync-grid dd {
  font-size: 12.5px;
  font-weight: 600;
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* ADVANCED DIAGNOSTICS ------------------------------------------- */
.dx-advanced-diag {
  margin-top: 14px;
  border-top: 1px dashed var(--dx-border);
  padding-top: 12px;
}
.dx-advanced-diag > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dx-faint);
}
.dx-advanced-diag > summary::-webkit-details-marker { display: none; }
.dx-advanced-diag > summary:hover { color: var(--dx-ink-soft); }
.dx-advanced-diag > summary .ph-caret-right { transition: transform 120ms ease; }
.dx-advanced-diag[open] > summary .ph-caret-right { transform: rotate(90deg); }
.dx-advanced-diag .status-fields { margin-top: 10px; }

/* GENERAL + RENDERING TABS --------------------------------------- */
.dx-device-card .dx-field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
@media (max-width: 540px) { .dx-device-card .dx-field-grid { grid-template-columns: 1fr; } }
.dx-device-card .dx-panel > .field + .field,
.dx-device-card .dx-panel > .field-grid + .field,
.dx-device-card .dx-panel > .field + .field-grid { margin-top: 16px; }
.dx-divider {
  border: 0;
  height: 1px;
  background: var(--dx-border-faint);
  margin: 18px 0;
}
.dx-quiet-hours .field-grid,
.dx-quiet-hours .dx-field-grid {
  transition: opacity 120ms ease;
}
.dx-quiet-hours[data-dep-off] [data-dep-target] {
  opacity: 0.45;
  pointer-events: none;
}
.dx-hint {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--dx-faint);
}
.dx-hint .ph-info { color: var(--dx-faint-2); margin-top: 1px; }
.dx-panel-orientation .device-subhead { margin-bottom: 10px; }
.dx-device-card input[type="range"] { accent-color: var(--dx-teal); }

/* Calibration tab: orientation card + colour test patterns +
   palette-recalibration placeholder. Each subsection is a plain
   block separated by dx-divider (matches Rendering + General). */
.dx-calib-section { margin: 4px 0 12px; }
.dx-calib-placeholder { opacity: 0.7; }
.dx-calib-placeholder .button[disabled] { cursor: not-allowed; }

.dx-pattern-picker {
  border: 0;
  padding: 0;
  margin: 0 0 14px;
  display: grid;
  gap: 8px;
}
.dx-pattern-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--dx-border-faint);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease;
}
.dx-pattern-option:hover { border-color: var(--dx-teal); }
.dx-pattern-option:has(input:checked) {
  border-color: var(--dx-teal);
  background: color-mix(in srgb, var(--dx-teal) 8%, transparent);
}
.dx-pattern-option input[type="radio"] { margin-top: 2px; }
.dx-pattern-body { display: flex; flex-direction: column; gap: 2px; }
.dx-pattern-label { font-weight: 600; }
.dx-pattern-desc { margin: 0; }

.dx-color-picker {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
}
.dx-color-picker[hidden] { display: none; }
.dx-color-chip {
  display: inline-block;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  border: 1px solid var(--dx-border-faint);
}

/* Two-column layout: controls (picker + colour + submit) on the left,
   preview stretches on the right and fills the vertical space so
   portrait panels are readable at a glance. Stacks under 720 px so
   the picker gets its full width on narrow viewports. */
.dx-test-pattern-grid {
  display: grid;
  grid-template-columns: minmax(280px, 1fr) minmax(280px, 1.2fr);
  gap: 18px;
  align-items: start;
}
.dx-test-pattern-controls {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
@media (max-width: 720px) {
  .dx-test-pattern-grid { grid-template-columns: 1fr; }
}

.dx-pattern-preview {
  position: relative;
  border: 1px solid var(--dx-border-faint);
  border-radius: 8px;
  background: repeating-conic-gradient(
    #f4f4f4 0 25%,
    #ffffff 0 50%
  ) 0 0 / 20px 20px;
  padding: 4px;
  margin: 0;
  text-align: center;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 340px;
}
.dx-pattern-preview img {
  max-width: 100%;
  max-height: 440px;
  display: block;
  object-fit: contain;
}
.dx-preview-caption {
  display: block;
  margin: 0;
}

.dx-pattern-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}

/* Palette recalibration card. Preset dropdown + attribution chip +
   save/export/import/delete actions live in this block. Save/import
   fold-outs use <details> so the primary picker stays uncluttered. */
.dx-palette-picker-row {
  display: grid;
  grid-template-columns: auto minmax(180px, 1fr) auto;
  gap: 10px;
  align-items: center;
  margin: 0 0 10px;
}
.dx-palette-picker-label {
  font-weight: 600;
  margin: 0;
}
.dx-palette-select { min-width: 240px; }
.dx-palette-attribution {
  margin: 0 0 4px;
  font-size: 12px;
}
.dx-palette-notes {
  margin: 0 0 12px;
  font-size: 12px;
  color: var(--dx-faint);
}
.dx-palette-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 12px;
}
.dx-palette-save,
.dx-palette-import {
  border: 1px dashed var(--dx-border-faint);
  border-radius: 6px;
  padding: 8px 12px;
  margin: 0 0 10px;
}
.dx-palette-save summary,
.dx-palette-import summary {
  cursor: pointer;
  font-weight: 600;
  padding: 2px 0;
}
.dx-palette-save[open] summary,
.dx-palette-import[open] summary { margin-bottom: 8px; }
.dx-palette-save-row {
  display: flex;
  gap: 8px;
  align-items: center;
  margin: 0 0 4px;
}
.dx-palette-save-row .input { flex: 1 1 auto; min-width: 180px; }
@media (max-width: 540px) {
  .dx-palette-picker-row { grid-template-columns: 1fr; }
  .dx-palette-save-row { flex-direction: column; align-items: stretch; }
}
:root[data-theme="dark"] .dx-palette-save,
:root[data-theme="dark"] .dx-palette-import { border-color: var(--t-border); }

/* Status-bar toggle card in the page editor (v0.72.0). Flat header
   row (title + switch) followed by the body panel; no expand/collapse
   since the body content is small enough to always show and the
   previous <details> shell made the section read as broken when the
   body was tiny. */
.dx-status-bar-toggle {
  border: 1px dashed var(--dx-border-faint);
  border-radius: 6px;
  padding: 8px 12px;
  margin: 12px 0 0;
}
.dx-status-bar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
}
.dx-status-bar-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
}
.dx-status-bar-switch { margin: 0; }
.dx-status-bar-body { padding: 8px 0 4px; }
.dx-status-bar-jump { margin: 8px 0 0; }
:root[data-theme="dark"] .dx-status-bar-toggle { border-color: var(--t-border); }

/* Per-cell "Layout tweaks" disclosure (v0.71.x). Hides the
   padding-override checkbox + slider by default so the cell edit card
   stays lean; the same block is where per-cell tweaks collect over
   time (e.g. per-edge padding, per-cell corner-radius overrides). */
.dx-cell-tweaks {
  border: 1px dashed var(--dx-border-faint);
  border-radius: 6px;
  padding: 6px 10px;
  margin: 10px 0 0;
}
.dx-cell-tweaks > summary {
  cursor: pointer;
  font-weight: 600;
  padding: 2px 0;
  font-size: 13px;
  color: var(--dx-text-muted, #56564f);
}
.dx-cell-tweaks[open] > summary { margin-bottom: 6px; }
.dx-cell-tweaks-body { padding: 4px 0 2px; }
.dx-cell-tweak-switch { margin: 0 0 8px; }
:root[data-theme="dark"] .dx-cell-tweaks { border-color: var(--t-border); }

.dx-tone-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin: 0 0 12px;
}
.dx-tone-slider {
  display: grid;
  /* Fixed-width label column so every slider aligns to the same start
     x. Previously ``minmax(160px, auto)`` grew with label text, so a
     long label like "LAB compress: max" pushed its slider right of
     the shorter ones. */
  grid-template-columns: 200px 1fr 44px;
  gap: 10px;
  align-items: center;
}
.dx-tone-label { font-weight: 600; }
.dx-tone-value {
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 40px;
}
.dx-tone-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
}
.dx-tone-select-row select { flex: 1 1 auto; }
.dx-tone-select-row .dx-tone-value { display: none; }
@media (max-width: 540px) {
  .dx-tone-slider { grid-template-columns: 1fr; }
  .dx-tone-value { text-align: left; }
}
.dx-palette-experimental {
  border: 1px dashed var(--dx-border-faint);
  border-radius: 6px;
  padding: 8px 12px;
  margin: 12px 0;
}
.dx-palette-experimental summary {
  cursor: pointer;
  font-weight: 600;
  padding: 2px 0;
}
.dx-palette-experimental::before {
  content: "experimental";
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  background: color-mix(in srgb, var(--dx-teal) 10%, transparent);
  color: var(--dx-teal);
  margin: 0 8px 0 0;
  float: right;
  position: relative;
  top: 4px;
}
:root[data-theme="dark"] .dx-palette-experimental { border-color: var(--t-border); }

/* Per-colour palette editor (v0.67.3). Grid of colour swatches so
   users can eyeball-match each palette entry to what their panel
   actually reproduces. */
.dx-palette-colors-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
  margin: 0 0 14px;
}
.dx-palette-color-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  border: 1px solid var(--dx-border-faint);
  border-radius: 8px;
  padding: 8px 10px;
  align-items: center;
}
.dx-palette-color-name { font-weight: 600; font-size: 13px; }
.dx-palette-color-input {
  width: 100%;
  height: 40px;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  padding: 0;
  background: transparent;
}
.dx-palette-color-hex {
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  color: var(--dx-faint);
  font-variant-numeric: tabular-nums;
}
:root[data-theme="dark"] .dx-palette-color-cell { border-color: var(--t-border); }

/* Delete-with-cascade prompt (v0.69.2, issue #48). ``<details>`` in
   the footer expands into an inline confirm with the wipe checkbox,
   so users can see what would be preserved vs wiped before pulling
   the trigger. */
.dx-delete-details { position: relative; }
.dx-delete-details > summary {
  list-style: none;
  cursor: pointer;
}
.dx-delete-details > summary::-webkit-details-marker { display: none; }
.dx-delete-form {
  position: absolute;
  right: 0;
  bottom: 40px;
  z-index: 5;
  width: min(340px, 90vw);
  padding: 12px 14px;
  border: 1px solid var(--dx-border);
  border-radius: 8px;
  background: var(--dx-surface, #ffffff);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}
.dx-delete-wipe {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 8px 0 0;
}
.dx-delete-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
:root[data-theme="dark"] .dx-delete-form {
  background: var(--t-surface);
  border-color: var(--t-border);
}

/* Per-device button map editor. Small textarea with a monospace
   input so the JSON reads cleanly, plus a fold-out that shows the
   resolved effective map (default + global + per-device). */
.dx-button-map {
  margin: 18px 0;
}
.dx-button-map .device-subhead-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 8px;
}
.dx-button-map-input {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 12px;
  line-height: 1.5;
}
.dx-button-map-effective {
  margin-top: 10px;
}
.dx-button-map-effective summary {
  cursor: pointer;
  font-size: 12px;
}
.dx-button-map-preview {
  margin: 8px 0 0;
  padding: 10px 12px;
  background: var(--band-tint, rgba(0, 0, 0, 0.03));
  border-radius: var(--t-radius, 6px);
  overflow-x: auto;
}
.dx-button-map-preview code {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 12px;
  white-space: pre;
}

/* SCHEDULE TAB --------------------------------------------------- */
.dx-empty {
  text-align: center;
  padding: 32px 22px;
}
.dx-empty-icon {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: var(--t-inset);
  color: var(--dx-faint);
  font-size: 20px;
  margin: 0 auto 12px;
}
.dx-empty-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--dx-ink);
  margin: 0 0 4px;
}
.dx-empty-help {
  font-size: 12.5px;
  color: var(--dx-faint);
  margin: 0 0 18px;
}
.dx-empty .button.primary {
  background: var(--dx-teal);
  color: var(--t-accent-fg);
  border: 0;
  padding: 8px 14px;
  border-radius: 7px;
  font-weight: 600;
  display: inline-flex;
  gap: 6px;
  align-items: center;
}
.dx-empty .button.primary:hover { background: var(--dx-teal-hover); }

/* STICKY SAVE BAR ------------------------------------------------
   v0.69.11: back to ``position: sticky`` with BOTH ``top`` and
   ``bottom`` insets so the bar sits at its natural in-card position
   when visible in the viewport, and sticks to the corresponding
   viewport edge only when the natural position would otherwise be
   off-screen. Setting both offsets lets sticky pick the appropriate
   edge based on scroll direction: user hasn't scrolled to the bar
   yet -> sticks to bottom, user scrolled past it -> sticks to top.
   The v0.69.10 always-fixed variant obscured page content unnecessarily
   when the natural position was already in view. */
.dx-save-bar {
  position: sticky;
  /* v0.69.12: ``--dx-save-bar-offset`` is set by settings.js when
     multiple bars are visible so they stack cumulatively upwards
     from the viewport bottom / downwards from the viewport top,
     instead of overlapping at the same sticky offset. */
  top: calc(20px + env(safe-area-inset-top, 0px) + var(--dx-save-bar-offset, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px) + var(--dx-save-bar-offset, 0px));
  margin: 14px 22px 18px;
  background: var(--dx-save-bar);
  /* The save bar is intentionally dark in both themes (--dx-save-bar), so
     its text stays light in both; --t-accent-fg would go near-black in dark. */
  color: #fff;
  border-radius: 10px;
  padding: 12px 14px 12px 18px;
  box-shadow: 0 8px 24px rgba(20, 20, 15, 0.22);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  animation: dx-slide-up 0.22s ease;
  z-index: 5;
}
.dx-save-bar[hidden] { display: none; }
.dx-save-bar-msg { font-size: 13px; }
.dx-save-bar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.dx-save-bar-discard,
.dx-save-bar-save {
  background: none;
  border: 0;
  padding: 7px 12px;
  border-radius: 7px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.dx-save-bar-discard { color: #d6d6d0; }
.dx-save-bar-discard:hover { color: #fff; }
.dx-save-bar-save {
  background: var(--dx-teal);
  color: var(--t-accent-fg);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dx-save-bar-save:hover { background: var(--dx-teal-hover-2); }
@keyframes dx-slide-up {
  from { transform: translateY(100%); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* FOOTER ACTION ROW --------------------------------------------- */
.dx-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 22px;
  background: var(--dx-footer-bg);
  border-top: 1px solid var(--dx-border-soft);
  flex-wrap: wrap;
}
.dx-footer-left,
.dx-footer-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.dx-card-footer .form--inline { margin: 0; }
.dx-footer-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  background: var(--dx-surface);
  border: 1px solid var(--t-border);
  border-radius: 7px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--dx-ink-soft);
  cursor: pointer;
}
.dx-footer-btn:hover { background: var(--dx-border-faint); }
.dx-footer-btn.dx-danger {
  color: var(--dx-danger);
  border-color: var(--dx-danger-border);
  background: transparent;
}
.dx-footer-btn.dx-danger:hover {
  background: var(--dx-danger-hover);
  border-color: color-mix(in oklab, var(--t-danger) 32%, transparent);
}
.dx-card-footer .calibrate-block { flex: 1 1 auto; margin: 0; }
.dx-card-footer .calibrate-prompt-block .calibrate-prompt { margin: 0 0 8px; }
.dx-card-footer .calibrate-prompt-block .calibrate-choices {
  display: flex;
  gap: 6px;
}
@media (max-width: 540px) {
  .dx-card-footer { flex-direction: column; align-items: stretch; }
  .dx-footer-left, .dx-footer-right { justify-content: flex-start; }
}

/* =====================================================================
   Server-tab section cards (handoff redesign). Reuses the same tokens
   as the device card; one card per logical group with a header row +
   stacked toggle rows. Master toggles live in the section header so
   dependent fields below can dim.
   ===================================================================== */

.dx-server-cards {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.dx-section-card {
  --dx-teal: #15846a;
  --dx-teal-hover: #127059;
  --dx-ink: #2b2b29;
  --dx-ink-soft: #56564f;
  --dx-faint: #9a9a93;
  --dx-faint-2: #a3a39b;
  --dx-border: #e6e5e0;
  --dx-border-faint: #f1f0eb;
  --dx-input-border: #dedcd6;
  --dx-inset: #faf9f6;

  background: var(--t-surface);
  border: 1px solid var(--dx-border);
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.04);
  padding: 22px 24px;
}
.dx-section-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
}
.dx-section-head > div { flex: 1 1 auto; min-width: 0; }
.dx-section-title {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--dx-ink);
}
.dx-section-desc {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--dx-faint);
}
.dx-section-meta {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--dx-faint);
}
.dx-section-meta code {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 12.5px;
  color: var(--dx-ink-soft);
  background: var(--dx-inset);
  border: 1px solid var(--t-border);
  border-radius: 5px;
  padding: 2px 7px;
  text-transform: none;
  letter-spacing: 0;
  margin-left: 6px;
}
.dx-section-card .field { margin-top: 12px; }
.dx-section-card .field-grid { margin-top: 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 540px) { .dx-section-card .field-grid { grid-template-columns: 1fr; } }
.dx-section-card .field-help { color: var(--dx-faint-2); font-size: 11.5px; }
.dx-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 15px 0;
  border-bottom: 1px solid var(--dx-border-faint);
}
.dx-toggle-row:last-child { border-bottom: 0; }
.dx-toggle-row-text { flex: 1 1 auto; min-width: 0; }
.dx-toggle-row-label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--dx-ink);
}
.dx-toggle-row-desc {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--dx-faint);
}
.dx-section-card[data-dep-off] [data-dep-target] {
  opacity: 0.45;
  pointer-events: none;
}
.dx-section-card[data-dep-off] [data-dep-target] input { pointer-events: none; }

/* Page header for redesigned settings (used by the server tab). */
.dx-settings-head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 6px;
}
.dx-settings-head-icon {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: var(--t-accent-soft);
  color: var(--t-accent);
  display: grid;
  place-items: center;
  font-size: 22px;
  flex: 0 0 auto;
}
.dx-settings-head h1 {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0;
}
.dx-settings-head-sub {
  margin: 4px 0 0;
  font-size: 13.5px;
  color: var(--t-muted);
}

/* Server-tab card visual overlay. Applied via .dx-server-area on the
   <div> wrapping the section loop in settings.html; restyles each
   .card to match the handoff section card while leaving the per-card
   markup unchanged. Only the visual treatment changes; field defs,
   form actions, and helper macros stay where they are. */
.dx-server-area > .card {
  --dx-teal: #15846a;
  --dx-ink: #2b2b29;
  --dx-faint: #9a9a93;
  --dx-border: #e6e5e0;
  --dx-border-faint: #f1f0eb;
  --dx-inset: #faf9f6;
  background: var(--t-surface);
  border: 1px solid var(--dx-border);
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.04);
  padding: 22px 24px;
  margin: 0 0 16px;
}
.dx-server-area > .card > .card-head {
  margin-bottom: 14px;
  align-items: flex-start;
}
.dx-server-area > .card > .card-head h2 {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--dx-ink);
  margin: 0;
}
.dx-server-area > .card > .card-head .lede,
.dx-server-area > .card > .card-head + .lede {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--dx-faint);
}
.dx-server-area > .card .meta dt {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--dx-faint);
}
.dx-server-area > .card .meta dd code {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 12.5px;
  color: var(--t-fg-soft);
  background: var(--dx-inset);
  border: 1px solid var(--t-border);
  border-radius: 5px;
  padding: 2px 7px;
}

/* ---------- Server tab: grouped section cards + toggle pattern ------ */

.dx-server-area { margin: 0; }
/* All section cards in the settings loop stack with a consistent
   16px gap, regardless of tab. Replaces the per-card
   ``margin-bottom`` the old .card style relied on so flex gap is
   the single source of truth for vertical rhythm. The ``*`` reset
   nukes both the legacy ``.card { margin: 0 0 16px }`` AND any
   default ``<form>`` user-agent margin so the first item sits
   flush against the tab bar (16px from the tab bar's own margin-
   bottom, same as every other tab). */
.settings-section-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.settings-section-list > * { margin: 0; }
.settings-section-list > form { margin: 0; padding: 0; }
.dx-server-cards { display: flex; flex-direction: column; gap: 16px; }
.dx-section-card { margin: 0; }
.dx-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding-bottom: 12px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--dx-border-faint, #f1f0eb);
}
.dx-section-head .dx-section-titleblock { flex: 1 1 auto; min-width: 0; }
.dx-section-icon {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: var(--t-accent-soft);
  color: var(--t-accent);
  font-size: 19px;
}
.dx-section-meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--dx-faint, #9a9a93);
}
.dx-section-body { display: flex; flex-direction: column; gap: 0; }
.dx-section-body .field {
  padding: 12px 0;
  margin: 0;
}
.dx-section-body .field + .field,
.dx-section-body .field + .dx-toggle-row,
.dx-section-body > .dx-toggle-row + .field,
.dx-section-body > .dx-toggle-row + .dx-toggle-row {
  border-top: 1px solid var(--dx-border-faint, #f1f0eb);
}
.dx-section-body .field label {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--dx-ink-soft, #56564f);
  margin-bottom: 7px;
  display: block;
}
.dx-section-body .field input:not([type="range"]):not([type="checkbox"]),
.dx-section-body .field select,
.dx-section-body .field textarea {
  border-radius: 8px;
  border: 1px solid var(--dx-input-border, #dedcd6);
  background: var(--t-surface);
  padding: 11px 13px;
  font-size: 14px;
  width: 100%;
}
/* Sliders carry their own track + thumb chrome via .field--slider,
   keep them out of the input-box treatment above (the rectangular
   border + padding wraps the range thumb in a halo otherwise). */
.dx-section-body .field--slider { padding-block: 14px; }
.dx-section-body .field .field-help {
  font-size: 11.5px;
  color: var(--dx-faint-2, #a3a39b);
  margin-top: 6px;
}

/* Full-row toggle (label + description left, switch right). */
.dx-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 15px 0;
}
.dx-toggle-row-text { flex: 1 1 auto; min-width: 0; }
.dx-toggle-row-label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--dx-ink, #2b2b29);
  cursor: pointer;
}
.dx-toggle-row-desc {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--dx-faint, #9a9a93);
  line-height: 1.45;
}

/* Visual switch (used by toggle rows and by master toggles in the
   section header). 46×26 track + 20px knob per the spec. */
.dx-toggle-switch {
  position: relative;
  display: inline-block;
  width: 46px;
  height: 26px;
  flex: 0 0 auto;
  cursor: pointer;
}
.dx-toggle-switch input {
  position: absolute;
  opacity: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
  z-index: 1;
}
.dx-toggle-track {
  position: absolute;
  inset: 0;
  background: var(--t-border-strong);
  border-radius: 999px;
  transition: background 0.15s ease;
}
.dx-toggle-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--t-surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease;
}
.dx-toggle-switch input:checked + .dx-toggle-track { background: var(--t-accent); }
.dx-toggle-switch input:checked + .dx-toggle-track .dx-toggle-knob {
  transform: translateX(20px);
}
.dx-toggle-switch input:focus-visible + .dx-toggle-track {
  outline: 2px solid var(--t-accent);
  outline-offset: 2px;
}

/* Sticky save bar variant for the server tab: fixed at the bottom of
   the viewport, centered, with the same dark surface + teal save
   button as the device card. ``--dx-save-bar`` + the teal tokens are
   scoped on the device card, so re-declare them here for the server
   variant (the bar lives at the page level, outside the card). */
.dx-save-bar--server {
  --dx-save-bar: #23231f;
  --dx-teal: #15846a;
  --dx-teal-hover-2: #179476;
  position: fixed;
  /* v0.69.17: the base ``.dx-save-bar`` sets ``top`` (for its
     sticky-positioned device-card variant). Without ``top: auto`` here
     the server variant inherits it, and ``position: fixed`` with both
     ``top`` and ``bottom`` set stretches the element to fill the
     viewport-height minus both insets, painting as a big black column. */
  top: auto;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  width: min(720px, calc(100% - 32px));
  z-index: 50;
  background: var(--dx-save-bar);
  /* The save bar is intentionally dark in both themes (--dx-save-bar), so
     its text stays light in both; --t-accent-fg would go near-black in dark. */
  color: #fff;
  border-radius: 10px;
  padding: 12px 14px 12px 18px;
  box-shadow: 0 8px 24px rgba(20, 20, 15, 0.22);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.dx-save-bar--server .dx-save-bar-msg { font-size: 13px; color: #fff; }
.dx-save-bar--server .dx-save-bar-discard { color: #d6d6d0; }
.dx-save-bar--server .dx-save-bar-discard:hover { color: #fff; }
.dx-save-bar--server .dx-save-bar-save {
  background: var(--dx-teal);
  color: var(--t-accent-fg);
}
.dx-save-bar--server .dx-save-bar-save:hover { background: var(--dx-teal-hover-2); }
.dx-save-bar--server .dx-save-bar-discard,
.dx-save-bar--server .dx-save-bar-save {
  background: transparent;
  border: 0;
  padding: 7px 12px;
  border-radius: 7px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.dx-save-bar--server .dx-save-bar-save {
  background: var(--dx-teal);
  color: var(--t-accent-fg);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dx-save-bar--server .dx-save-bar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Hide the inline Save button on the legacy server-tab forms (Broker
   + Virtual panel) since they now use the sticky save bar instead. */
.dx-legacy-server-form > .actions { display: none; }
@keyframes dx-slide-up-fixed {
  from { transform: translateX(-50%) translateY(120%); opacity: 0; }
  to { transform: translateX(-50%) translateY(0); opacity: 1; }
}
.dx-save-bar--server:not([hidden]) { animation: dx-slide-up-fixed 0.22s ease; }

/* Section dependent-dim: applied when the section header's master
   toggle is off. JS toggles ``data-dep-off`` on the section card. */
.dx-section-card[data-dep-off] .dx-section-body {
  opacity: 0.45;
  pointer-events: none;
}
.dx-section-card[data-dep-off] .dx-section-body input,
.dx-section-card[data-dep-off] .dx-section-body select {
  background: var(--dx-inset, #faf9f6);
}


/* =====================================================================
   Dark-theme overrides for the handoff-redesigned cards. Toggled via
   ``<html data-theme="dark">`` (same hook the rest of the app uses).
   Cards keep their layout + spacing; only colours flip. Hard-coded
   hex values in the dx-* rules above are the light-mode source of
   truth; the block below maps them onto the slate-leaning dark
   palette declared in ``base.css``. The teal accent + the warm
   smart-sync band stay constant so they read as status colour in
   both modes. The save bar is intentionally always dark, so it
   stays as-is across themes.
   ===================================================================== */

:root[data-theme="dark"] .dx-device-card,
:root[data-theme="dark"] .dx-section-card {
  background: var(--t-surface);
  border-color: var(--t-border);
  color: var(--t-fg);
  box-shadow: var(--t-shadow-sm), var(--t-shadow-inset);
}
:root[data-theme="dark"] .dx-device-card:hover {
  border-color: var(--t-border-strong);
  box-shadow: var(--t-shadow), var(--t-shadow-inset);
}

/* Header strip */
:root[data-theme="dark"] .dx-device-card .dx-card-header,
:root[data-theme="dark"] .dx-section-card .dx-section-head {
  border-bottom-color: var(--t-border);
}
:root[data-theme="dark"] .dx-device-card .dx-header-icon,
:root[data-theme="dark"] .dx-section-card .dx-section-icon {
  background: color-mix(in oklab, var(--t-accent) 22%, var(--t-surface));
  color: var(--t-accent);
}
:root[data-theme="dark"] .dx-device-card .dx-title { color: var(--t-fg); }
:root[data-theme="dark"] .dx-device-card .dx-transport-badge {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
  color: var(--t-fg-soft);
}
:root[data-theme="dark"] .dx-device-card .dx-status-line,
:root[data-theme="dark"] .dx-device-card .dx-status-muted { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-device-card .dx-collapse-btn {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
  color: var(--t-fg);
}
:root[data-theme="dark"] .dx-device-card .dx-collapse-btn:hover {
  background: var(--t-surface-raised);
  border-color: var(--t-border-strong);
}

/* Connection details */
:root[data-theme="dark"] .dx-conn-details { border-bottom-color: var(--t-border); }
:root[data-theme="dark"] .dx-conn-details > summary { color: var(--t-muted); }
:root[data-theme="dark"] .dx-conn-details > summary:hover { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-conn-grid dt { color: var(--t-muted); }
:root[data-theme="dark"] .dx-conn-grid dd { color: var(--t-fg); }
:root[data-theme="dark"] .dx-reveal-btn { color: var(--t-accent); }
:root[data-theme="dark"] .dx-reveal-btn:hover { color: var(--t-accent-hover); }

/* Tabs */
:root[data-theme="dark"] .dx-tabs { border-bottom-color: var(--t-border); }
:root[data-theme="dark"] .dx-tab { color: var(--t-muted); }
:root[data-theme="dark"] .dx-tab:hover,
:root[data-theme="dark"] .dx-tab:focus,
:root[data-theme="dark"] .dx-tab:focus-visible { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-tab.is-active { color: var(--t-accent); }

/* Status tab tiles */
:root[data-theme="dark"] .dx-stat-tile {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
}
:root[data-theme="dark"] .dx-stat-label { color: var(--t-muted); }
:root[data-theme="dark"] .dx-stat-value { color: var(--t-fg); }
:root[data-theme="dark"] .dx-stat-sub { color: var(--t-muted); }
:root[data-theme="dark"] .dx-bar { background: var(--t-border-strong); }
:root[data-theme="dark"] .dx-bar.is-on { background: var(--t-ok); }
:root[data-theme="dark"] .dx-muted { color: var(--t-muted); }

/* Smart sync panel keeps its warm tones intentionally (status
   indicator), but bumps the band background so the contrast against
   a dark card body still reads. */
:root[data-theme="dark"] .dx-smart-sync { border-color: var(--t-border); }
:root[data-theme="dark"] .dx-smart-sync-head {
  background: color-mix(in oklab, #e0902f 16%, var(--t-surface));
  border-bottom-color: color-mix(in oklab, #e0902f 30%, var(--t-surface));
}
:root[data-theme="dark"] .dx-smart-sync-label { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-smart-sync-confidence { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-smart-sync-meter {
  background: color-mix(in oklab, #e0902f 22%, var(--t-surface));
}
:root[data-theme="dark"] .dx-smart-sync-explainer { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-smart-sync-grid > div { border-top-color: var(--t-border); }
:root[data-theme="dark"] .dx-smart-sync-grid > div:nth-child(2n) { border-left-color: var(--t-border); }
:root[data-theme="dark"] .dx-smart-sync-grid dt { color: var(--t-muted); }
:root[data-theme="dark"] .dx-smart-sync-grid dd { color: var(--t-fg); }

/* Advanced diagnostics + Schedule empty state */
:root[data-theme="dark"] .dx-advanced-diag {
  border-top-color: var(--t-border);
}
:root[data-theme="dark"] .dx-advanced-diag > summary { color: var(--t-muted); }
:root[data-theme="dark"] .dx-advanced-diag > summary:hover { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-empty-icon {
  background: var(--t-surface-soft);
  color: var(--t-muted);
}
:root[data-theme="dark"] .dx-empty-title { color: var(--t-fg); }
:root[data-theme="dark"] .dx-empty-help { color: var(--t-muted); }

/* General + Rendering tab field treatment */
:root[data-theme="dark"] .dx-divider { background: var(--t-border); }
:root[data-theme="dark"] .dx-pattern-option { border-color: var(--t-border); }
:root[data-theme="dark"] .dx-pattern-option:has(input:checked) {
  border-color: var(--t-accent);
  background: color-mix(in srgb, var(--t-accent) 12%, transparent);
}
:root[data-theme="dark"] .dx-pattern-preview {
  border-color: var(--t-border);
  background: repeating-conic-gradient(
    #22262b 0 25%,
    #2a2f34 0 50%
  ) 0 0 / 20px 20px;
}
:root[data-theme="dark"] .dx-color-chip { border-color: var(--t-border); }
:root[data-theme="dark"] .dx-hint { color: var(--t-muted); }
:root[data-theme="dark"] .dx-hint .ph-info { color: var(--t-muted); }

/* Footer action row */
:root[data-theme="dark"] .dx-card-footer {
  background: var(--t-surface-soft);
  border-top-color: var(--t-border);
}
:root[data-theme="dark"] .dx-footer-btn {
  background: var(--t-surface);
  border-color: var(--t-border);
  color: var(--t-fg);
}
:root[data-theme="dark"] .dx-footer-btn:hover {
  background: var(--t-surface-raised);
  border-color: var(--t-border-strong);
}
:root[data-theme="dark"] .dx-footer-btn.dx-danger {
  color: var(--t-danger);
  border-color: color-mix(in oklab, var(--t-danger) 50%, transparent);
  background: transparent;
}
:root[data-theme="dark"] .dx-footer-btn.dx-danger:hover {
  background: var(--t-danger-bg);
  border-color: var(--t-danger);
}

/* Server-tab section card chrome */
:root[data-theme="dark"] .dx-section-title { color: var(--t-fg); }
:root[data-theme="dark"] .dx-section-desc { color: var(--t-muted); }
:root[data-theme="dark"] .dx-section-meta { color: var(--t-muted); }
:root[data-theme="dark"] .dx-section-meta code {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
  color: var(--t-fg-soft);
}
:root[data-theme="dark"] .dx-section-head {
  border-bottom-color: var(--t-border);
}

/* Server-tab section body: inputs, labels, separators, slider. */
:root[data-theme="dark"] .dx-section-body .field + .field,
:root[data-theme="dark"] .dx-section-body .field + .dx-toggle-row,
:root[data-theme="dark"] .dx-section-body > .dx-toggle-row + .field,
:root[data-theme="dark"] .dx-section-body > .dx-toggle-row + .dx-toggle-row {
  border-top-color: var(--t-border);
}
:root[data-theme="dark"] .dx-section-body .field label { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-section-body .field input:not([type="range"]):not([type="checkbox"]),
:root[data-theme="dark"] .dx-section-body .field select,
:root[data-theme="dark"] .dx-section-body .field textarea {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
  color: var(--t-fg);
}
:root[data-theme="dark"] .dx-section-body .field .field-help { color: var(--t-muted); }

/* Toggle row (label-left / switch-right) */
:root[data-theme="dark"] .dx-toggle-row-label { color: var(--t-fg); }
:root[data-theme="dark"] .dx-toggle-row-desc { color: var(--t-muted); }
:root[data-theme="dark"] .dx-toggle-track { background: var(--t-border-strong); }
:root[data-theme="dark"] .dx-toggle-switch input:checked + .dx-toggle-track {
  background: var(--t-accent);
}

/* Dependent-dim background gets a slightly sunk tint instead of the
   light-mode cream wash. */
:root[data-theme="dark"] .dx-section-card[data-dep-off] .dx-section-body input,
:root[data-theme="dark"] .dx-section-card[data-dep-off] .dx-section-body select {
  background: var(--t-surface-sunk);
}

/* Devices tab redesign (#17 discovered strip). Inset rows pick up the
   sunk-surface tone; sub-labels + count pills shift to the slate
   palette; the listening dot keeps its teal accent. */
:root[data-theme="dark"] .dx-discovered-row {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
}
:root[data-theme="dark"] .dx-discovered-id code { color: var(--t-fg); }
:root[data-theme="dark"] .dx-meta-k { color: var(--t-muted); }
:root[data-theme="dark"] .dx-meta-v { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-discovered-meta code { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-discovered-explainer { color: var(--t-muted); }
:root[data-theme="dark"] .dx-discovered-sublabel { color: var(--t-muted); }
:root[data-theme="dark"] .dx-count-pill {
  color: var(--t-accent);
  background: color-mix(in oklab, var(--t-accent) 22%, var(--t-surface));
}
:root[data-theme="dark"] .dx-listening { color: var(--t-muted); }
:root[data-theme="dark"] .dx-listening-dot {
  background: var(--t-accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--t-accent) 30%, transparent);
}
:root[data-theme="dark"] .dx-discovered-hint { color: var(--t-muted); }
:root[data-theme="dark"] .dx-discovered-hint code {
  background: rgba(255, 255, 255, 0.06);
}
:root[data-theme="dark"] .dx-btn-primary-sm { background: var(--t-accent); color: var(--t-accent-fg); }
:root[data-theme="dark"] .dx-btn-primary-sm:hover { background: var(--t-accent-hover); }
:root[data-theme="dark"] .dx-btn-ghost-sm {
  border-color: var(--t-border);
  color: var(--t-fg-soft);
}
:root[data-theme="dark"] .dx-btn-ghost-sm:hover {
  background: var(--t-surface-raised);
  border-color: var(--t-border-strong);
  color: var(--t-fg);
}

/* Add device (#16) dark mode */
:root[data-theme="dark"] .dx-hint-band {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
  color: var(--t-fg-soft);
}
:root[data-theme="dark"] .dx-hint-band strong { color: var(--t-fg); }
:root[data-theme="dark"] .dx-warn-band {
  background: color-mix(in oklab, #e0902f 16%, var(--t-surface));
  border-color: color-mix(in oklab, #e0902f 30%, var(--t-surface));
  color: #f3c97e;
}
:root[data-theme="dark"] .dx-warn-band .ph-warning { color: #f3c97e; }
:root[data-theme="dark"] .dx-warn-band a { color: var(--t-accent); }
:root[data-theme="dark"] .dx-warn-band a:hover { color: var(--t-accent-hover); }
:root[data-theme="dark"] .dx-segmented-label { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-segmented {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
}
:root[data-theme="dark"] .dx-segmented-btn { color: var(--t-muted); }
:root[data-theme="dark"] .dx-segmented-btn:hover { color: var(--t-fg-soft); }
:root[data-theme="dark"] .dx-segmented-btn.is-active {
  background: var(--t-surface-raised);
  color: var(--t-fg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
:root[data-theme="dark"] .dx-segmented-help { color: var(--t-muted); }
:root[data-theme="dark"] .dx-btn-primary { background: var(--t-accent); color: var(--t-accent-fg); }
:root[data-theme="dark"] .dx-btn-primary:hover { background: var(--t-accent-hover); }

/* Kind defaults rows (#22) dark mode */
:root[data-theme="dark"] .dx-kind-row {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
}
:root[data-theme="dark"] .dx-kind-summary:hover { background: rgba(255, 255, 255, 0.03); }
:root[data-theme="dark"] .dx-kind-icon {
  background: color-mix(in oklab, var(--t-accent) 22%, var(--t-surface));
  color: var(--t-accent);
}
:root[data-theme="dark"] .dx-kind-name { color: var(--t-fg); }
:root[data-theme="dark"] .dx-kind-id code { color: var(--t-muted); }
:root[data-theme="dark"] .dx-kind-cta { color: var(--t-accent); }
:root[data-theme="dark"] .dx-modified-badge {
  color: #f3c97e;
  background: color-mix(in oklab, #e0902f 22%, var(--t-surface));
}
:root[data-theme="dark"] .dx-plugin-source { color: var(--t-muted); }
:root[data-theme="dark"] .dx-inline-confirm {
  background: color-mix(in oklab, #e0902f 14%, var(--t-surface));
  border-color: color-mix(in oklab, #e0902f 30%, var(--t-surface));
  color: #f3c97e;
}
:root[data-theme="dark"] .dx-inline-confirm code { color: #f3c97e; }
:root[data-theme="dark"] .dx-btn-danger-sm {
  color: var(--t-danger);
  border-color: color-mix(in oklab, var(--t-danger) 50%, transparent);
}
:root[data-theme="dark"] .dx-btn-danger-sm:hover {
  background: var(--t-danger-bg);
  border-color: var(--t-danger);
}

/* Legacy ``.dx-server-area > .card`` overlay (still used by any
   non-converted card on the server tab). */
:root[data-theme="dark"] .dx-server-area > .card {
  background: var(--t-surface);
  border-color: var(--t-border);
  box-shadow: var(--t-shadow-sm), var(--t-shadow-inset);
}
:root[data-theme="dark"] .dx-server-area > .card > .card-head h2 { color: var(--t-fg); }
:root[data-theme="dark"] .dx-server-area > .card .meta dt { color: var(--t-muted); }
:root[data-theme="dark"] .dx-server-area > .card .meta dd code {
  background: var(--t-surface-soft);
  border-color: var(--t-border);
  color: var(--t-fg-soft);
}


/* =====================================================================
   Devices tab redesign (issues #16 / #17 / #22).
   Reuses .dx-section-card + .dx-section-* chrome. The selectors below
   are the new bits: discovered sub-groups, inset rows, sub-labels,
   segmented controls, hint bands, kind-row collapsibles. Dark-mode
   overrides live in the block further down with the rest of the
   data-theme="dark" overrides.
   ===================================================================== */

/* ---------- Listening pulse (right side of Discovered header) ------- */
.dx-listening {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--t-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.dx-listening-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--t-accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, #15846a 25%, transparent);
  animation: dx-pulse-listen 2s ease-in-out infinite;
}
@keyframes dx-pulse-listen {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.85); }
}

/* ---------- Discovered sub-group ----------------------------------- */
.dx-discovered-group { margin-top: 14px; }
.dx-discovered-group:first-child { margin-top: 0; }
.dx-discovered-sublabel {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--t-muted);
}
.dx-count-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--t-accent);
  background: var(--t-accent-soft);
  border-radius: 999px;
  text-transform: none;
}
.dx-discovered-explainer {
  margin: 6px 0 10px;
  font-size: 12px;
  color: var(--t-muted);
  line-height: 1.45;
}
.dx-discovered-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

/* ---------- Discovered row (inset card) ---------------------------- */
.dx-discovered-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 15px;
  background: var(--t-inset);
  border: 1px solid var(--t-border);
  border-radius: 10px;
}
.dx-discovered-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  flex: 1 1 auto;
}
.dx-discovered-id code {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--t-fg);
  background: transparent;
  padding: 0;
}
.dx-discovered-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: 11.5px;
}
.dx-meta-pair {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.dx-meta-k {
  font-weight: 600;
  color: var(--t-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 10.5px;
}
.dx-meta-v {
  color: var(--t-fg-soft);
}
.dx-discovered-meta code {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 11.5px;
  color: var(--t-fg-soft);
  background: transparent;
  padding: 0;
}
.dx-discovered-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  flex-wrap: wrap;
}
.dx-discovered-actions .form--inline { margin: 0; }
.dx-discovered-hint {
  font-size: 11.5px;
  color: var(--t-muted);
}
.dx-discovered-hint code {
  background: rgba(0, 0, 0, 0.05);
  border-radius: 4px;
  padding: 1px 4px;
}

/* ---------- Small primary / ghost button (used in row actions) ---- */
.dx-btn-primary-sm,
.dx-btn-ghost-sm {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 11px;
  border-radius: 7px;
  cursor: pointer;
  border: 1px solid transparent;
  white-space: nowrap;
}
.dx-btn-primary-sm {
  background: var(--t-accent);
  color: var(--t-accent-fg);
}
.dx-btn-primary-sm:hover { background: var(--t-accent-hover); }
.dx-btn-ghost-sm {
  background: transparent;
  border-color: var(--t-border);
  color: var(--t-fg-soft);
}
.dx-btn-ghost-sm:hover {
  background: var(--t-surface);
  border-color: var(--t-border-strong);
  color: var(--t-fg);
}

/* ---------- Mobile collapse for Discovered rows ------------------- */
@media (max-width: 640px) {
  .dx-discovered-row { flex-direction: column; align-items: flex-start; }
  .dx-discovered-actions { width: 100%; justify-content: flex-start; }
}


/* ---------- Add device (issue #16): hint band, warn band, segmented
   control, pair row, primary button. The card itself reuses the
   shared .dx-section-card chrome. ---------------------------------- */

.dx-hint-band {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 14px;
  padding: 10px 12px;
  background: var(--t-inset);
  border: 1px solid var(--t-border);
  border-radius: 9px;
  font-size: 12px;
  color: var(--t-fg-soft);
  line-height: 1.5;
}
.dx-hint-band .ph-info {
  color: var(--t-muted);
  flex: 0 0 auto;
  font-size: 14px;
  margin-top: 1px;
}
.dx-hint-band p { margin: 0; }
.dx-hint-band strong { color: var(--t-fg); font-weight: 600; }

.dx-warn-band {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 14px;
  padding: 10px 12px;
  background: var(--t-warn-bg);
  border: 1px solid var(--t-pill-warn-border);
  border-radius: 9px;
  font-size: 12px;
  color: var(--t-warn);
  line-height: 1.5;
}
.dx-warn-band .ph-warning { color: var(--t-warn); flex: 0 0 auto; font-size: 14px; margin-top: 1px; }
.dx-warn-band p { margin: 0; }
.dx-warn-band a {
  color: var(--t-accent);
  font-weight: 600;
  text-decoration: none;
}
.dx-warn-band a:hover { color: var(--t-accent-hover); text-decoration: underline; }

/* Segmented control (transport) */
.dx-segmented-group {
  margin: 0 0 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.dx-segmented-label {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--t-fg-soft);
}
.dx-segmented {
  display: inline-flex;
  background: var(--t-surface-sunk);
  border: 1px solid var(--t-border);
  border-radius: 9px;
  padding: 3px;
  gap: 2px;
  align-self: flex-start;
}
.dx-segmented-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--t-muted);
  border-radius: 7px;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, box-shadow 120ms ease;
}
.dx-segmented-btn:hover { color: var(--t-fg-soft); }
.dx-segmented-btn.is-active {
  background: var(--t-surface);
  color: var(--t-fg);
  font-weight: 700;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.06);
}
.dx-segmented-help {
  margin: 0;
  font-size: 11.5px;
  color: var(--t-muted);
  line-height: 1.5;
}

/* Pair row: note input on the left, action button on the right. The
   input's label sits above it, so flex-end lines the button's bottom up
   with the input field (not the label). */
.dx-pair-row {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}
.dx-pair-input { flex: 1 1 320px; min-width: 0; }
/* The field wrapper carries 12px block padding + a top margin elsewhere;
   drop both here so the field box ends at the input's bottom edge and the
   flex-end button lines up with the input rather than sitting ~12px below
   it (the padding-bottom). */
.dx-pair-input .field { margin-top: 0; padding: 0; }
.dx-pair-action { flex: 0 0 auto; }
.dx-pair-action .dx-btn-primary { white-space: nowrap; }

/* Narrow viewports (device preview panes, phones): stack the button
   under the note and let it span the row instead of overflowing the
   card edge. */
@media (max-width: 560px) {
  .dx-pair-input { flex-basis: 100%; }
  .dx-pair-action { flex: 1 1 100%; }
  .dx-pair-action .dx-btn-primary { width: 100%; justify-content: center; }
}

.dx-pending-codes { margin-top: 14px; }

/* Add-device branch (REST or MQTT). Wrapping div so the existing
   .form's grid layout cascades cleanly. */
.dx-add-branch { margin-top: 4px; }

/* Right-aligned action button row for the MQTT add form. */
.dx-actions-right {
  display: flex;
  justify-content: flex-end;
  margin-top: 14px;
}

/* Larger primary button (used for "Issue pairing code" + "Add device"). */
.dx-btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 9px 16px;
  border: 0;
  border-radius: 8px;
  background: var(--t-accent);
  color: var(--t-accent-fg);
  cursor: pointer;
  transition: background 120ms ease;
}
.dx-btn-primary:hover { background: var(--t-accent-hover); }
.dx-btn-primary .ph { font-size: 14px; }

.dx-btn-danger-sm {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 11px;
  border-radius: 7px;
  border: 1px solid var(--t-pill-danger-border);
  background: transparent;
  color: var(--t-danger);
  cursor: pointer;
}
.dx-btn-danger-sm:hover { background: var(--t-danger-bg); border-color: color-mix(in oklab, var(--t-danger) 32%, transparent); }


/* ---------- Kind defaults rows (issue #22) ------------------------- */

.dx-kind-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.dx-kind-row {
  background: var(--t-inset);
  border: 1px solid var(--t-border);
  border-radius: 10px;
  overflow: hidden;
}
.dx-kind-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 15px;
  background: transparent;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.dx-kind-summary:hover { background: rgba(0, 0, 0, 0.02); }
.dx-kind-icon {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: var(--t-accent-tint);
  color: var(--t-accent);
  font-size: 16px;
}
.dx-kind-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dx-kind-name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 650;
  color: var(--t-fg);
}
.dx-kind-id code {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 11.5px;
  color: var(--t-muted);
  background: transparent;
  padding: 0;
}
.dx-kind-cta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--t-accent);
}
.dx-kind-chevron {
  transition: transform 0.18s ease;
  font-size: 13px;
}
.dx-kind-row.is-open .dx-kind-chevron { transform: rotate(180deg); }
.dx-kind-cta .when-open { display: none; }
.dx-kind-row.is-open .dx-kind-cta .when-open { display: inline; }
.dx-kind-row.is-open .dx-kind-cta .when-closed { display: none; }
.dx-kind-form {
  padding: 0 15px 15px;
}
.dx-kind-form > form { display: contents; }

.dx-modified-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--t-warn);
  background: var(--t-warn-bg);
  border-radius: 5px;
}
.dx-plugin-source {
  font-size: 11px;
  font-weight: 500;
  color: var(--t-muted);
  text-transform: none;
  letter-spacing: 0;
}

.dx-kind-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.dx-inline-confirm {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  padding: 10px 12px;
  background: var(--t-warn-bg);
  border: 1px solid var(--t-pill-warn-border);
  border-radius: 9px;
  color: var(--t-warn);
  font-size: 12.5px;
  flex-wrap: wrap;
}
.dx-inline-confirm code {
  background: transparent;
  font-family: ui-monospace, Menlo, monospace;
  color: var(--t-warn);
  font-weight: 600;
}
.dx-inline-confirm-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex: 0 0 auto;
}


/* =====================================================================
   v0.56 admin-uplift shared components (PR 0).

   The .dx-section-card chrome and most of the patterns the rest of
   the admin pages need landed during v0.54. This block adds the
   remaining shared primitives so every uplifted page can reach for
   the same building blocks:

   * .dx-pill .is-ok / .is-warn / .is-danger / .is-neutral — status
     chips with the spec's pill palette
   * .dx-dow-pill — selectable day-of-week chip
   * .dx-input — the spec's input baseline (existing fields keep
     their forms.css styling; this is for hand-rolled inputs inside
     uplifted pages)
   * .dx-code + syntax-highlight class set, paired with the in-house
     ``static/pages/json-highlight.js`` tokenizer
   * .dx-meta-strip — small inline meta line under section titles
     (used by Rotations' "anchor / cycle / steps" line, etc.)
   ===================================================================== */

/* Status pills. ``.is-neutral`` is the count badge tone; tones swap
   to spec values from the v0.56 handoff and flip in dark mode via
   the --t-pill-* override block in base.css. */
.dx-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 6px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: var(--t-pill-neutral-bg);
  color: var(--t-pill-neutral-fg);
  border: 1px solid transparent;
}
.dx-pill.is-ok {
  background: var(--t-pill-ok-bg);
  color: var(--t-pill-ok-fg);
  border-color: var(--t-pill-ok-border);
}
.dx-pill.is-warn {
  background: var(--t-pill-warn-bg);
  color: var(--t-pill-warn-fg);
  border-color: var(--t-pill-warn-border);
}
.dx-pill.is-danger {
  background: var(--t-pill-danger-bg);
  color: var(--t-pill-danger-fg);
  border-color: var(--t-pill-danger-border);
}
.dx-pill-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
}

/* Day-of-week pill (radio-style selectable chip). Used by Schedules
   + Rotations. Selected state = accent fill. */
.dx-dow-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  padding: 6px 12px;
  font-size: 11.5px;
  font-weight: 600;
  border-radius: 18px;
  background: var(--t-surface);
  border: 1px solid var(--t-border);
  color: var(--t-fg-soft);
  cursor: pointer;
  user-select: none;
}
.dx-dow-pill input { position: absolute; opacity: 0; pointer-events: none; }
.dx-dow-pill:hover { border-color: var(--t-border-strong); color: var(--t-fg); }
.dx-dow-pill:has(input:checked),
.dx-dow-pill.is-selected {
  background: var(--t-accent);
  border-color: var(--t-accent);
  color: var(--t-accent-fg);
}

/* Standalone input baseline. ``forms.css`` handles every field
   rendered via the ``.field`` macros; this is the lighter-weight
   shape for hand-rolled inputs inside section cards (the spec
   calls this the "input" baseline: 8px radius, 11×13 padding,
   white bg, 1px border). */
.dx-input {
  width: 100%;
  padding: 11px 13px;
  font-size: 14px;
  border-radius: 8px;
  border: 1px solid var(--t-input-border, var(--t-border));
  background: var(--t-surface);
  color: var(--t-fg);
  font-family: inherit;
}
.dx-input:focus {
  outline: 2px solid var(--t-accent);
  outline-offset: -1px;
  border-color: var(--t-accent);
}
/* A <select> carrying .dx-input sheds its OS chrome and picks up the same
   custom caret the form-macro selects use, so it reads as a sibling of the
   text inputs it sits beside (e.g. the dashboard Grid/Freeform picker). */
select.dx-input {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  padding-right: 34px;
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--t-fg-soft) 50%),
    linear-gradient(135deg, var(--t-fg-soft) 50%, transparent 50%);
  background-position:
    calc(100% - 18px) 50%,
    calc(100% - 13px) 50%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
select.dx-input:focus,
select.dx-input:hover {
  background-image:
    linear-gradient(45deg, transparent 50%, var(--t-accent) 50%),
    linear-gradient(135deg, var(--t-accent) 50%, transparent 50%);
}

/* Inset row: shared shape behind Discovered devices, History entries,
   Widgets index rows, Schedule conditions, Events rows. The
   ``.dx-discovered-row`` class already exists; this is the more
   general selector for other pages to opt into. */
.dx-inset-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 15px;
  background: var(--t-inset);
  border: 1px solid var(--t-inset-border);
  border-radius: 10px;
}

/* Code block (the spec's warm-taupe surface for webhook examples,
   event payloads, condition JSON). Sits visually distinct from the
   section card. JSON syntax-highlight tokens overlay on top via the
   in-house json-highlight.js. */
.dx-code {
  margin: 0;
  padding: 14px 16px;
  background: var(--t-code-bg);
  border: 1px solid var(--t-code-border);
  border-radius: 10px;
  color: var(--t-code-fg);
  font-family: var(--t-font-mono);
  font-size: 12.5px;
  line-height: 1.55;
  overflow-x: auto;
  white-space: pre;
  tab-size: 2;
}
.dx-code-key { color: var(--t-code-key); }
.dx-code-str { color: var(--t-code-str); }
.dx-code-num { color: var(--t-code-num); }
.dx-code-kw { color: var(--t-code-kw); font-weight: 600; }
.dx-code-punct { color: var(--t-code-punct); }

/* Meta strip: small horizontal row of icon-prefixed key/value pairs.
   Used under rotation titles ("anchor 07:00 · 60 min cycle · 2
   steps"), under schedule rows, etc. */
.dx-meta-strip {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 14px;
  font-size: 11.5px;
  color: var(--t-muted);
}
.dx-meta-strip > span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.dx-meta-strip .ph { color: var(--t-muted); font-size: 13px; }
.dx-meta-strip code {
  background: transparent;
  padding: 0;
  font-family: var(--t-font-mono);
  font-size: 11.5px;
  color: var(--t-fg-soft);
}

/* Disclosure (▸) pattern: caret-prefixed inline summary that toggles
   a body. Used by Conditions, Edit raw JSON, Example call. The
   summary keeps the row clickable; the body sits flush below with a
   1px top divider. */
.dx-disclosure { border-top: 1px solid var(--t-border); padding-top: 12px; margin-top: 12px; }
.dx-disclosure > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--t-fg-soft);
  user-select: none;
}
.dx-disclosure > summary::-webkit-details-marker { display: none; }
.dx-disclosure > summary:hover { color: var(--t-fg); }
.dx-disclosure > summary .ph-caret-right {
  transition: transform 120ms ease;
  font-size: 12px;
}
.dx-disclosure[open] > summary .ph-caret-right { transform: rotate(90deg); }
.dx-disclosure-body { margin-top: 10px; }

/* Icon-only ghost button variant (delete actions on Dashboards,
   History, etc). Same chrome as .dx-btn-ghost-sm but pinned to a
   consistent square so a row of icon-only buttons reads as a
   balanced row regardless of each glyph's intrinsic width.
   ``min-width: 32px`` previously meant a row of three buttons
   with mixed glyph widths (pause + paper-plane + pencil) rendered
   as three different sizes — pencil-simple is intrinsically
   narrower than paper-plane-tilt so it hit the floor and read
   visually smaller next to the others. */
.dx-btn-icon-only {
  padding: 0;
  width: 34px;
  height: 34px;
  min-width: 34px;
  justify-content: center;
}
.dx-btn-icon-only .ph { font-size: 14px; }

/* When an icon-only sibling sits in the same actions row as text
   buttons (e.g. Edit + Push + Duplicate + Delete on the Dashboards
   list, or the equivalent on Devices / History), pin the text
   variants to the same 34px square height as ``.dx-btn-icon-only``
   so the row reads as a balanced strip rather than a jagged mix.
   Pre-fix the text variants were content-sized (~30px) so the
   icon-only squares stood proud of them at narrow widths where the
   row wraps. */
.dx-discovered-actions .dx-btn-primary-sm,
.dx-discovered-actions .dx-btn-ghost-sm {
  min-height: 34px;
  box-sizing: border-box;
}

/* Dashboards list (Tier 1.1). The dashboard row is built on
   .dx-inset-row + .dx-section-icon + a meta strip; class names below
   are dashboard-specific to keep the row layout decoupled from the
   shared inset shape. */
.dx-dashboard-row { align-items: center; }
.dx-dashboard-main {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1 1 auto;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.dx-dashboard-icon {
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border-radius: 10px;
}
.dx-dashboard-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.dx-dashboard-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--t-fg);
  letter-spacing: -0.005em;
}
.dx-meta-k {
  font-weight: 600;
  color: var(--t-muted);
  text-transform: uppercase;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  margin-right: 4px;
}
.dx-meta-v { color: var(--t-fg-soft); font-size: 11.5px; }

/* Filter strip used across History + Events. Sits as a top-of-page
   tab nav with count-pill counters; active chip gets the accent
   underline same as the device card tabs. */
.dx-filter-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
  margin-bottom: 14px;
  padding: 0 2px;
}
.dx-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 999px;
  background: transparent;
  color: var(--t-muted);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid var(--t-border);
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}
.dx-filter-chip:hover { color: var(--t-fg); border-color: var(--t-border-strong); }
.dx-filter-chip.is-active {
  background: var(--t-accent-tint);
  color: var(--t-accent);
  border-color: var(--t-accent-tint);
}
.dx-filter-chip .dx-count-pill { background: var(--t-pill-neutral-bg); color: var(--t-pill-neutral-fg); }
.dx-filter-chip.is-active .dx-count-pill {
  background: var(--t-accent);
  color: var(--t-accent-fg);
}

/* ============================================================
 *  6b. PAGE — History (/history)
 *     EXTRACTED → static/style/dx-history.css (v0.63.7).
 * ============================================================ */

/* Page-head primary CTA: small primary button positioned under (or
   right of) the page title + lede. Used on Dashboards' "New
   dashboard", Widgets' "Browse community widgets", etc. */
.page-head-cta {
  margin-top: 12px;
}
.page-head-cta .dx-btn-primary {
  display: inline-flex;
}

/* Widgets index (Tier 1.3). Per-kind section cards + widget rows
   with icon + name + meta + admin badge + description. */
.dx-widget-row { align-items: flex-start; padding: 14px 15px; }
.dx-widget-icon {
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border-radius: 10px;
  margin-top: 1px;
}
.dx-widget-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 auto;
  min-width: 0;
}
.dx-widget-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 14px;
}
.dx-widget-name { font-weight: 700; color: var(--t-fg); }
.dx-widget-id {
  font-family: var(--t-font-mono);
  font-size: 11.5px;
  color: var(--t-muted);
  background: transparent;
  padding: 0;
}
.dx-widget-desc {
  margin: 0;
  font-size: 12.5px;
  color: var(--t-fg-soft);
  line-height: 1.5;
}

/* Loader-errors section card variant uses the danger-band tone so it
   reads as an attention-grabber. */
.dx-section-card--danger {
  border-color: var(--t-pill-danger-border);
  background: var(--t-pill-danger-bg);
}

/* ============================================================
 *  6a. PAGE — Battery dashboard (/devices/battery)
 *     EXTRACTED → static/style/dx-battery.css (v0.63.5).
 *     Banner kept so the file map stays accurate; rules live
 *     in the dedicated sheet now.
 * ============================================================ */

/* ============================================================
 *  6c. PAGE — Events (/events)
 *     EXTRACTED → static/style/dx-events.css (v0.63.7).
 * ============================================================ */

/* All Events page rules (.dx-events-toolbar, .dx-event-row,
   .dx-event-row--<type> per-type swatch, .dx-filter-chip per-type
   colour, .dx-event-summary / -icon / -body / -head / -meta /
   -caret / -expand / -thumb-link) now live in dx-events.css. */

/* ============================================================
 *  6d. ADAPTER — legacy ``card_head`` macro inside dx-section-card
 *     Bridges pages that haven't moved off the legacy card_head
 *     macro yet (settings.html, page_editor.html, login.html,
 *     setup.html). Once those pages flip to inline
 *     ``<header class="dx-section-head">``, this block can be
 *     deleted.
 * ============================================================ */

/* Pragmatic adapter: pages that wrap their content in
   ``.dx-section-card`` but still call the legacy ``card_head``
   macro (which emits ``<header class="card-head">``) pick up the
   v0.56 header chrome via this rule. Lets Tier 3+ pages flip just
   the outer card class without touching every ``card_head`` call.
   The macro's ``.icon-box`` -> teal-tinted square mapping happens
   naturally because the existing ``.icon-box`` already styles to
   the right shape; we just need to nudge the size + alignment. */
.dx-section-card > .card-head {
  padding: 0;
  margin: 0 0 14px;
}
.dx-section-card > .card-head .card-head-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.dx-section-card > .card-head h2,
.dx-section-card > .card-head h3 {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--t-fg);
}
.dx-section-card > .card-head .icon-box {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--t-accent-tint);
  color: var(--t-accent);
  font-size: 18px;
  display: grid;
  place-items: center;
}
.dx-section-card > .card-head .card-head-action {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* v0.60: consistent 16px vertical rhythm between stacked
   ``.dx-section-card`` siblings at the page level. Earlier pages
   relied on the legacy ``.card { margin: 0 0 16px }`` rule which
   only fires for the legacy class; sibling section cards landed
   flush. Combined with the per-page card chrome this gives every
   admin page the same breathing room without per-page overrides.
   Pages that want a single fused card (dashboards) put their two
   surfaces inside a single ``section_card`` macro call. */
.dx-section-card + .dx-section-card { margin-top: 16px; }

/* ============================================================
 *  6e. PAGE — Dashboards (/pages)
 *     EXTRACTED → static/style/dx-dashboards.css (v0.63.6).
 * ============================================================ */

/* History thumb + row-gap rules consolidated into
   ``static/style/dx-history.css`` in v0.63.7. */

/* ============================================================
 *  6f. PAGE — Widgets Browse (/plugins/browse)
 *     EXTRACTED → static/style/dx-marketplace.css (v0.63.7).
 * ============================================================ */

/* ============================================================
 *  6g. PAGE — Settings → About
 *     Three flat cards: project meta, survey CTA, sponsor CTA.
 *     The page reuses .meta from shell.css, but overrides the
 *     flex-wrap row to a two-column grid because the values here
 *     are long URLs and a wrap-row reads as untidy. .actions-end
 *     right-aligns the lone CTA on the survey + sponsor cards
 *     so the button anchors to the card edge instead of
 *     floating in the middle.
 * ============================================================ */

.settings-about .lede {
  margin: var(--t-space-2) 0 var(--t-space-3);
  color: var(--t-fg-soft);
  max-width: 64ch;
}

.settings-about .about-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: var(--t-space-4);
  row-gap: var(--t-space-2);
  margin-top: var(--t-space-3);
}

.settings-about .about-meta div {
  display: contents;
}

.settings-about .about-meta dt {
  font-size: var(--t-size-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--t-muted);
  margin: 0;
  align-self: center;
}

.settings-about .about-meta dd {
  margin: 0;
  font-size: var(--t-size-sm);
  color: var(--t-fg);
  align-self: center;
}

.settings-about .about-meta dd code {
  font-size: var(--t-size-sm);
}

.settings-about .actions-end {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--t-space-3);
}
