/* Eira — slim app layer over Pico CSS.
 *
 * Pico provides the base (typography, light/dark, forms, articles, nav).  This file only:
 *   1. retunes Pico's primary to the Eira brand teal, with AA-contrast variants per scheme;
 *   2. adds app-specific bits Pico has no primitive for — status dots, badges, the card grid;
 *   3. nudges sizing up for legibility / touch (hard-of-hearing, possibly older audience).
 * Keep it small: prefer Pico's semantic defaults over new classes. */

/* ---- Brand accent -------------------------------------------------------- */
:root {
  /* Eira teal (#2ec4c6) for filled controls; link/primary text is darkened for AA on light. */
  --pico-primary: #0f7e80;
  --pico-primary-background: #2ec4c6;
  --pico-primary-hover-background: #1fa3a0;
  --pico-primary-hover: #0a5e60;
  --pico-primary-focus: rgba(46, 196, 198, 0.4);
  --pico-primary-underline: rgba(15, 126, 128, 0.5);
  --pico-border-radius: 0.5rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Lighten the teal for contrast on the dark ground; keep filled buttons on-brand. */
    --pico-primary: #5fdedf;
    --pico-primary-background: #2ec4c6;
    --pico-primary-hover-background: #4fd5d6;
    --pico-primary-hover: #8aeaeb;
    --pico-primary-focus: rgba(79, 213, 214, 0.35);
    --pico-primary-underline: rgba(95, 222, 223, 0.5);
  }
}

[data-theme="dark"] {
  --pico-primary: #5fdedf;
  --pico-primary-background: #2ec4c6;
  --pico-primary-hover-background: #4fd5d6;
  --pico-primary-hover: #8aeaeb;
  --pico-primary-focus: rgba(79, 213, 214, 0.35);
  --pico-primary-underline: rgba(95, 222, 223, 0.5);
}

/* Slightly larger base type + comfortable touch targets for legibility/a11y. */
:root {
  --pico-font-size: 106.25%; /* ~17px base */
}
/* Comfortable touch target for the interactive controls — but NOT the small toggles
 * (checkbox / radio / switch) or the range track, which Pico sizes itself; forcing a tall
 * min-height on those distorts them. */
button,
[role="button"],
select,
summary,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]) {
  min-height: 2.75rem; /* ~44px touch target */
}

/* ---- Top nav / brand ----------------------------------------------------- */
/* A taller, calmer navbar: extra vertical breathing room around every nav element. */
header.container > nav {
  --pico-nav-element-spacing-vertical: 0.6rem;
  align-items: center;
}
header.container > nav > ul:first-child {
  flex: 0 0 auto;
}
header.container > nav > ul:last-child {
  min-width: 0;
}
.brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
/* Present the logo as a rounded app-tile — bigger and more confident than the old 1.9rem chip,
   the teal ground rounded off so it reads as a mark rather than a stray square. */
.brand img {
  display: block;
  width: 4.5rem;
  height: 4.5rem;
  min-width: 4.5rem;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 24%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  flex: 0 0 auto;
}
/* Nav links carry a leading icon; lay icon + label out inline and centered. */
nav li > a {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
nav a[aria-current="page"] {
  color: var(--pico-primary);
  font-weight: 600;
}

/* On narrower screens the header keeps the logo stable and lets the action nav wrap
   into two rows. Secondary pages stay in the top row; Routing and Adapters move closer
   to the page content in the bottom row. */
@media (max-width: 72rem) {
  header.container > nav {
    align-items: flex-start;
    gap: 0.75rem;
  }
  header.container > nav > ul:last-child {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    justify-content: flex-end;
    row-gap: 0.35rem;
    column-gap: 0.65rem;
    padding-top: 0.2rem;
  }
  header.container > nav > ul:last-child::after {
    content: "";
    flex-basis: 100%;
    order: 1;
  }
  header.container > nav > ul:last-child > li:nth-child(1),
  header.container > nav > ul:last-child > li:nth-child(2) {
    order: 2;
  }
  header.container > nav > ul:last-child > li:nth-child(3),
  header.container > nav > ul:last-child > li:nth-child(4),
  header.container > nav > ul:last-child > li:nth-child(5) {
    order: 0;
  }
}

@media (max-width: 40rem) {
  .brand img {
    width: 3.75rem;
    height: 3.75rem;
    min-width: 3.75rem;
  }
  header.container > nav > ul:last-child {
    column-gap: 0.45rem;
  }
}

/* ---- Icons --------------------------------------------------------------- */
/* Monochromatic, inherit the surrounding text/brand color, scale with font-size. */
.nav-icon {
  width: 1.2em;
  height: 1.2em;
  flex: none;
}
/* Heading marks (page <h1> and section <h2>): a calm brand-teal accent before the title. */
.heading-icon {
  width: 1em;
  height: 1em;
  flex: none;
  margin-right: 0.55rem;
  color: var(--pico-primary);
  vertical-align: -0.12em;
}
/* Routing card headers: in/out arrow badge sits left of the card name. */
.io-head {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
.io-badge {
  display: inline-flex;
  align-items: center;
  color: var(--pico-primary);
}
.io-arrow {
  /* Height-locked; width follows the per-kind viewBox so the wider (landscape)
     output glyphs keep the same arc size as the square input glyph. */
  height: 1.6em;
  width: auto;
  flex: none;
}

/* ---- Theme switcher ------------------------------------------------------ */
/* Anchor the absolute menu to the details box (not the viewport). */
.theme-switcher {
  position: relative;
}
/* Render Pico's dropdown summary as a borderless icon button (drop the box + chevron). */
.theme-switcher > summary {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}
.theme-switcher > summary::after {
  display: none;
}
.theme-switcher > summary:not([role]) {
  border-color: transparent;
  background: transparent;
}
.theme-switcher > summary:hover,
.theme-switcher[open] > summary {
  background: var(--pico-secondary-background);
}
/* Size the menu to its content and pin it to the trigger's right edge.
   Pico targets `details.dropdown > summary + ul` with higher specificity, so match
   that selector here to reliably override its default left-aligned dropdown. */
details.dropdown.theme-switcher > summary + ul {
  right: 0;
  left: auto;
  width: max-content;
  min-width: max-content;
  max-width: calc(100vw - 1rem);
  transform-origin: top right;
}
.theme-switcher svg {
  width: 1.15em;
  height: 1.15em;
}
.theme-switcher > ul a {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* ---- Page headings ------------------------------------------------------- */
/* Pico flushes the hgroup title and subtitle together and leaves only ~1rem below the group.
   Give the subtitle a little air under the title, and a clearer gap before the page body
   (the Adapters sections, the routing/WiFi cards). */
hgroup > :first-child:not(:last-child) {
  margin-bottom: 0.5rem;
}
hgroup {
  margin-bottom: 2.25rem;
}

/* ---- Card grids ---------------------------------------------------------- */
.section-title {
  margin: 1.5rem 0 0.75rem;
}
.card-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}
@media (min-width: 768px) {
  .card-grid {
    /* Min in rem (not px) so it scales with the rem-sized segmented mode bar as Pico bumps the root
       font on wider screens — the card stays a touch wider than the bar, so the pills never wrap. */
    grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
  }
}
.card-grid > article {
  margin: 0; /* grid gap handles spacing */
}
article > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
article > header .adapter-name {
  font-weight: 600;
  font-size: 1.05rem;
}

/* ---- Status dot + short label ------------------------------------------- */
.status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  white-space: nowrap;
}
.status::before {
  content: "";
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--dot, var(--pico-muted-border-color));
  box-shadow: 0 0 0 0.18rem color-mix(in srgb, var(--dot, transparent) 22%, transparent);
}
.status-connected,
.status-streaming {
  --dot: #1fae5a;
}
.status-busy,
.status-connecting {
  --dot: #e0962e;
}
.status-idle {
  --dot: #9aa6ad;
}
.status-error {
  --dot: #d6453d;
}

/* ---- Badges (capability, wired/wireless, maturity) ----------------------- */
.badge {
  display: inline-block;
  padding: 0.12rem 0.55rem;
  border-radius: 1rem;
  font-size: 0.74rem;
  font-weight: 600;
  line-height: 1.5;
  background: var(--pico-secondary-background);
  color: var(--pico-secondary-inverse);
}
.badge-soft {
  background: color-mix(in srgb, var(--pico-primary-background) 18%, transparent);
  color: var(--pico-primary);
}
/* The adapter currently routed as the live input — filled brand teal so it reads as "the one". */
.badge-active-input {
  background: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
  margin-left: 0.4rem;
}
/* Mode controls (Bluetooth adapter card): two segmented bars (direction + transport) laid out by a
   flex row that wraps. Side by side when the card is wide enough; stacked when it is not. */
.mode-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  margin: 0 0 0.6rem;
}
/* Each bar is a self-contained segmented control: a radio group rendered as a pill strip. It never
   breaks internally (nowrap), so when space runs out the whole bar wraps to the next row rather than
   splitting mid-pill. Pills size to their label, so wide labels like "LE Audio" never clip. The
   hidden <input> sits right before its <span>, so the adjacent-sibling combinator styles the
   selected/disabled/focus states — no :has() needed. */
.segmented {
  display: flex;
  flex-wrap: nowrap;
  /* Pico sets `fieldset { width: 100% }`; override so each bar is only as wide as its pills, lets
     the two sit side by side, and stops the pills stretching to fill the card. */
  flex: 0 0 auto;
  width: auto;
  max-width: 100%;
  border: 1px solid var(--pico-form-element-border-color);
  border-radius: 0.5rem;
  overflow: hidden;
}
.segmented label {
  margin: 0;
  display: flex;
}
/* Visually hide the native radio; its sibling <span> is the visible button. */
.segmented input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.segmented span {
  padding: 0.34rem 0.7rem;
  font-size: 0.82rem;
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
  border-left: 1px solid var(--pico-form-element-border-color);
  transition: background 0.12s ease;
}
.segmented label:first-of-type span {
  border-left: 0;
}
.segmented input:checked + span {
  background: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
}
.segmented input:not(:checked):not(:disabled) + span:hover {
  background: var(--pico-secondary-background);
}
/* Off direction makes transport inapplicable: every transport radio is disabled, dimming each. */
.segmented input:disabled + span {
  opacity: 0.5;
  cursor: not-allowed;
}
/* Keyboard focus ring, since the input itself is invisible. */
.segmented input:focus-visible + span {
  outline: 2px solid var(--pico-primary);
  outline-offset: -2px;
}
/* Pico gives every fieldset a 1rem bottom margin; inside the flex row it cannot collapse and
   becomes dead space trapped at the bottom of .mode-controls. Compensating on the next element
   (the old -1rem on .discoverable-toggle) broke whenever that element was display:none'd away
   (ASHA/output-only cards hide the toggle), pushing the error/note banners ~1rem lower there.
   Zero it at the source so the spacing below the bars is the same on every card. */
.mode-controls fieldset {
  margin-bottom: 0;
}

/* The switch is a sub-option of the input mode above it (the identity MAC/USB lines now live up
   in the header subtitle, not between); the gap above is the .mode-controls margin. Wider gap
   below to the device list. */
.discoverable-toggle {
  margin: 0 0 1.6rem;
}

/* Discoverable only applies to an input adapter, so couple its visibility directly to the Input
   switch: hidden whenever Input is unchecked. This is driven by the live checkbox state, so the
   line appears/disappears the instant you toggle Input — no wait for the /mode round-trip or the
   10 s poll. (ASHA keeps Input disabled+unchecked, so its discoverable line stays hidden too.) */
article:not(:has(.direction-toggles input[name="input"]:checked)) .discoverable-toggle {
  display: none;
}

/* The transport choice only means something while a direction is on, and clicking a radio on a
   deactivated card just re-renders it back (irritating no-op) — so hide the bar whenever both
   direction switches are off. CSS-hidden only, never removed: the checked radio must keep
   submitting with the /mode form so re-activating a direction restores the remembered transport
   instead of the A2DP server default. Driven by live checkbox state like .discoverable-toggle
   above, so the bar appears the instant a direction is switched on. */
article:not(:has(.direction-toggles input:checked)) .mode-controls .segmented {
  display: none;
}

.maturity {
  font-size: 0.8rem;
  color: var(--pico-muted-color);
  margin: 0.35rem 0 0;
}
.maturity-experimental {
  color: #c77d12;
}
.maturity-planned {
  color: var(--pico-del-color, #b3261e);
}
/* Direction toggles (both adapter cards): independent on/off switches (input and/or output) — the
   multi-select counterpart to the single-select transport bar beside them on Bluetooth cards. Laid
   out as an inline row that wraps; keeps Pico's fieldset bottom margin so it lines up with the
   segmented bar and spaces the audio card's badges below. */
.direction-toggles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1rem;
  flex: 0 0 auto;
  width: auto;
}
.direction-toggles label {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  white-space: nowrap;
}
.direction-toggles input[type="checkbox"] {
  margin: 0;
}
/* ASHA is output-only: the disabled Input toggle (and its label) dims to read as not-applicable. */
.direction-toggles label:has(input:disabled) {
  opacity: 0.5;
  cursor: not-allowed;
}
/* Line/Mic capture-source picker on a dual-input audio card — an inline radio row with a small inline
   legend, sitting just under the direction toggles (same compact type scale). */
.input-source-choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1rem;
  border: 0;
  padding: 0;
}
.input-source-choice .input-source-label {
  font-size: 0.85rem;
  font-weight: bold;
}
.input-source-choice label {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  white-space: nowrap;
}
.input-source-choice input[type="radio"] {
  margin: 0;
}
/* Unobtrusive one-line identity subtitle directly under the adapter name (MAC · connection/USB). The
   header carries a 0.75rem bottom margin, so pull it up to sit tight under the name, then leave a
   small gap before the direction controls below. */
.adapter-meta {
  font-size: 0.78rem;
  color: var(--pico-muted-color);
  margin: -0.55rem 0 0.65rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.45;
}
/* Action-failure banner on an adapter card (e.g. a remove that BlueZ rejected). */
.card-error {
  font-size: 0.85rem;
  color: var(--pico-del-color, #b3261e);
  border: 1px solid var(--pico-del-color, #b3261e);
  border-radius: var(--pico-border-radius);
  padding: 0.5rem 0.75rem;
  margin: 0.75rem 0;
}

/* Informational banner on an adapter card (guidance, not a fault — e.g. "assign this hearing
   aid to an ear on the Routing page"). Muted, so it reads as a hint next to the red .card-error. */
.card-note {
  font-size: 0.85rem;
  color: var(--pico-muted-color);
  border: 1px solid var(--pico-muted-border-color, var(--pico-muted-color));
  border-radius: var(--pico-border-radius);
  padding: 0.5rem 0.75rem;
  margin: 0.75rem 0;
}

/* Card-level transport-honesty banner (the connected device is not using the selected transport,
   e.g. LE Audio selected but the connection is classic A2DP). Amber like .adapter-warning — an
   expectation warning, not an action failure; sits in the maturity hint's slot under the mode bar. */
.card-warning {
  font-size: 0.85rem;
  color: #8a5a00;
  background: #fff7e6;
  border: 1px solid #e0a800;
  border-radius: var(--pico-border-radius);
  padding: 0.5rem 0.75rem;
  margin: 0.35rem 0 0;
}

/* Page-level advisory banner above the adapter sections (e.g. a sound card claimed by another
   login session). Amber, not the red .card-error, since it's a misconfiguration warning rather
   than an action failure. */
.adapter-warning {
  font-size: 0.9rem;
  color: #8a5a00;
  background: #fff7e6;
  border: 1px solid #e0a800;
  border-radius: var(--pico-border-radius);
  padding: 0.6rem 0.85rem;
  margin: 0 0 1rem;
}

/* Inline pairing-failure hint under a scan row. A failed pair leaves the row in place (htmx skips
   the delete on 4xx); the Pair button toggles .pair-failed on the row to reveal this. Scoped to the
   scan list so the row can wrap the hint onto its own line without affecting the paired list. */
.scan-results .device-list li {
  flex-wrap: wrap;
}
.scan-error {
  display: none;
  flex-basis: 100%;
  font-size: 0.8rem;
  color: var(--pico-del-color, #b3261e);
  margin-top: 0.15rem;
}
li.pair-failed .scan-error {
  display: block;
}

/* Full-width guidance line under a paired-device row (same wrap trick as .scan-error) — e.g. a
   hearing aid listed on an adapter whose transport is not ASHA, where classic Connect cannot
   reach it. Wrap is scoped via :has so rows without a hint keep the single-line layout. */
.device-list li:has(.row-hint) {
  flex-wrap: wrap;
}
.row-hint {
  flex-basis: 100%;
  font-size: 0.8rem;
  color: var(--pico-muted-color);
  margin: 0;
}

/* ---- Device list --------------------------------------------------------- */
.device-list {
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0;
}
.device-list li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0;
  border-top: 1px solid var(--pico-muted-border-color);
}
.device-list li:first-child {
  border-top: 0;
}
.device-list .device-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.device-list .device-addr {
  display: block;
  font-size: 0.74rem;
  color: var(--pico-muted-color);
}
.device-list .row-actions {
  display: flex;
  gap: 0.35rem;
}
.device-list .row-actions button {
  margin: 0;
  padding: 0.3rem 0.7rem;
  min-height: 2.2rem;
  font-size: 0.82rem;
  width: auto;
}
.empty-note {
  color: var(--pico-muted-color);
  font-size: 0.88rem;
  margin: 0.5rem 0 0;
}

/* ---- Add device ---------------------------------------------------------- */
/* A single scan button (no disclosure) sits below the device list with clear separation. */
.add-device {
  margin-top: 1rem;
}
/* Auto-width outline button — distinct from the filled per-row "Pair" buttons, and not the
   full-bleed bar Pico gives buttons by default, which dwarfed the rest of the card. */
.add-device-btn {
  width: auto;
  margin: 0;
  padding: 0.4rem 0.9rem;
  min-height: 2.4rem;
  font-size: 0.88rem;
}
/* Idle vs. busy labels: htmx adds .htmx-request to the button while its request is in flight (the
   scan "Scanning…" and the Pair "Pairing…"). Show the spinner + busy label then, the plain label
   otherwise. Keep the button content centered so the swap doesn't shift its width awkwardly. */
.busy-label-btn .btn-idle,
.busy-label-btn .btn-busy {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  justify-content: center;
}
.busy-label-btn .btn-busy {
  display: none;
}
.busy-label-btn.htmx-request .btn-idle {
  display: none;
}
.busy-label-btn.htmx-request .btn-busy {
  display: inline-flex;
}
/* Keep the busy button readable (the global .htmx-request dim would wash out the spinner). */
.busy-label-btn.htmx-request {
  opacity: 1;
}
.spinner {
  width: 1em;
  height: 1em;
  flex: none;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation-duration: 1.6s;
  }
}
.add-device .scan-results:not(:empty) {
  margin-top: 0.75rem;
}

/* Header above the found-devices list: a count on the left, "Clear" on the right. Clear empties
   the whole scan-results container (a pure client-side DOM op — results are never cached
   server-side), removing the leftover discoveries that linger after the wanted device is paired. */
.scan-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.scan-count {
  color: var(--pico-muted-color);
  font-size: 0.85rem;
}

/* Compact outline button, matching the "Scan to add a device" button's sizing so the two read as
   a pair rather than the full-bleed bar Pico gives buttons by default. */
.scan-clear {
  width: auto;
  min-width: 4.5em;
  margin: 0;
  padding: 0.3rem 0.8rem;
  min-height: 2.2rem;
  font-size: 0.82rem;
}

/* Match the per-row "Pair" button's width to "Clear" so the two read as an even pair (the idle
   "Pair" label is short; min-width keeps it from looking cramped next to Clear). */
.scan-results .device-list .row-actions button {
  min-width: 4.5em;
  padding: 0.3rem 0.8rem;
}

/* ---- Routing controls ---------------------------------------------------- */
.control {
  margin: 0.9rem 0;
}
.control > label {
  font-weight: 600;
  margin-bottom: 0.3rem;
}
.range-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0;
}
.range-row input[type="range"] {
  flex: 1;
  margin: 0;
}
.range-row .value {
  font-variant-numeric: tabular-nums;
  min-width: 3.4rem;
  text-align: right;
  font-weight: 600;
}
.range-row .inc,
.range-row .dec {
  width: 2.75rem;
  flex: 0 0 auto;
  padding: 0;
  font-size: 1.3rem;
  line-height: 1;
}

/* JS-blocked banner (Pico-styled via inline role). */
.js-warning {
  display: none;
}

/* ---- HTMX busy affordance ------------------------------------------------ */
.htmx-request,
.htmx-request.status {
  opacity: 0.6;
}

/* The Adapters page poller (#adapter-cards) refreshes in the background on a timer; it must not
   dim the whole section like a user-initiated action does. Opt it out of the busy affordance so the
   automatic refresh is invisible — this also prevents a "stuck dim" if a poll is aborted mid-flight
   (e.g. by hx-sync) and the htmx-request class lingers. The id beats the class on specificity. */
#adapter-cards.htmx-request {
  opacity: 1;
}

/* ---- WiFi page (JS-rendered markup) -------------------------------------- */
.info-value {
  font-weight: 600;
  margin: 0 0 0.5rem;
}
.muted-note,
.status-message {
  color: var(--pico-muted-color);
  font-size: 0.9rem;
  white-space: pre-line;
}
.wifi-list {
  margin-top: 0.5rem;
}
.wifi-list-item {
  padding: 0.6rem 0;
  border-top: 1px solid var(--pico-muted-border-color);
}
.wifi-list-item:first-child {
  border-top: 0;
}
.wifi-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
}
.wifi-details {
  min-width: 0;
}
.wifi-connection-status {
  display: block;
  font-size: 0.78rem;
  color: var(--pico-muted-color);
}
.wifi-actions {
  display: flex;
  gap: 0.4rem;
}
.wifi-actions button,
.scan-select-btn {
  margin: 0;
  width: auto;
  padding: 0.35rem 0.8rem;
  min-height: 2.3rem;
  font-size: 0.85rem;
}
.secondary-btn {
  --pico-background-color: var(--pico-secondary-background);
  --pico-border-color: var(--pico-secondary-border, var(--pico-secondary-background));
  --pico-color: var(--pico-secondary-inverse);
}
.wifi-password-row {
  display: none;
  gap: 0.4rem;
  margin-top: 0.5rem;
}
.wifi-password-row.active {
  display: flex;
}
.wifi-password-input {
  flex: 1;
  margin: 0;
}
.disabled-action {
  opacity: 0.6;
  pointer-events: none;
}

/* Shared confirmation modal (partials/confirm_dialog.html + confirm-dialog.js).
   The Pico <dialog> supplies the overlay, card, and layout; we only narrow the
   card and style the destructive confirm action. */
#confirm-dialog > article {
  max-width: 28rem;
}
#confirm-dialog #confirm-dialog-message {
  margin-bottom: 0;
  white-space: pre-line;
}
/* Header is the app's flex row (title left, close right); drop Pico's close-button
   vertical margins so the icon stays vertically centered against the title. */
#confirm-dialog > article > header > [rel="prev"] {
  margin-top: 0;
  margin-bottom: 0;
}
/* Destructive confirm button (e.g. remove/forget). */
button.destructive {
  --pico-background-color: var(--pico-del-color, #b3261e);
  --pico-border-color: var(--pico-del-color, #b3261e);
  --pico-color: #fff;
}
button.destructive:is(:hover, :active, :focus) {
  --pico-background-color: #9c2019;
  --pico-border-color: #9c2019;
}

/* ---- 404 page ------------------------------------------------------------ */
/* Centered, calm — a big watermark code over a friendly note and route-back links. */
.notfound {
  text-align: center;
  max-width: 32rem;
  margin: 3rem auto;
}
.notfound-code {
  font-size: clamp(4rem, 18vw, 7rem);
  font-weight: 700;
  line-height: 1;
  margin: 0 0 0.5rem;
  color: var(--pico-primary);
  opacity: 0.85;
  letter-spacing: 0.05em;
}
.notfound hgroup {
  margin-bottom: 1.75rem;
}
.notfound code {
  word-break: break-all;
}
.notfound-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
}
.notfound-links a[role="button"] {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}

/* ===== Hardware monitor ================================================== */
/* The /hardware page: a responsive grid of Pico <article> metric cards, each
   a list of label/value rows, plus thin temp/load bars and yes/no health
   colouring. Polled (no page reload) by the page's inline script. */
.hw-hero {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.hw-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.hw-actions button {
  margin: 0;
  width: auto;
}
.hw-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: 1rem;
}
.hw-grid > article {
  margin: 0;
}
.metric-list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.metric-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.metric-row > span {
  color: var(--pico-muted-color);
  font-size: 0.9rem;
}
.metric-row > strong {
  text-align: right;
  word-break: break-word;
}
.metric-row-stack {
  flex-direction: column;
  align-items: stretch;
}
.metric-row-stack > strong {
  text-align: left;
  white-space: pre-line;
}
/* Thin progress bar used for CPU temperature/load. */
.metric-bar {
  height: 0.4rem;
  border-radius: 0.4rem;
  background: var(--pico-secondary-background);
  overflow: hidden;
}
.metric-bar > span {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 0.4rem;
  background: var(--pico-primary-background);
  transition: width 0.4s ease;
}
/* Yes/No health colouring for the power-throttle + PipeWire rows. */
.metric-ok {
  color: #1fae5a;
}
.metric-warn {
  color: #d6453d;
}


/* --------------------------------------------------------------------------
 * Hardware / ASHA
 * -------------------------------------------------------------------------- */

.asha-card-header {
    align-items: center;
    display: flex;
    justify-content: space-between;
    gap: 1rem;
}

.asha-health-box {
    align-items: flex-end;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.asha-health {
    border-radius: 999px;
    font-size: 0.85rem;
    padding: 0.25rem 0.65rem;
    white-space: nowrap;
}

.asha-health-reason {
    color: var(--muted-color);
    font-size: 0.78rem;
    line-height: 1.2;
    max-width: 18rem;
    text-align: right;
}

.asha-health-ok {
    background: rgba(39, 174, 96, 0.14);
    color: #1e7f46;
}

.asha-health-warn {
    background: rgba(245, 166, 35, 0.16);
    color: #9a6400;
}

.asha-health-bad {
    background: rgba(231, 76, 60, 0.14);
    color: #a93226;
}

.asha-health-unknown {
    background: rgba(127, 140, 141, 0.14);
    color: #5f6a6a;
}

.asha-summary {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    margin-bottom: 1rem;
}

.asha-summary-item {
    border: 1px solid var(--muted-border-color);
    border-radius: 0.75rem;
    padding: 0.75rem;
}

.asha-summary-item span,
.asha-summary-item small {
    color: var(--muted-color);
    display: block;
}

.asha-summary-item strong {
    display: block;
    font-size: 1.15rem;
    margin: 0.25rem 0;
}

.asha-summary-item small {
    font-size: 0.78rem;
    line-height: 1.25;
}

.asha-section-title {
    color: var(--muted-color);
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    margin: 1rem 0 0.35rem;
    text-transform: uppercase;
}

.asha-metric-list .metric-row strong {
    white-space: nowrap;
}

.asha-metric-trigger {
    background: rgba(231, 76, 60, 0.12);
    border-radius: 0.4rem;
    color: #a93226;
    padding: 0.05rem 0.35rem;
}
