/* =====================================================================
   components.css -- THE CONTROLS BOTH APPLICATIONS DRAW, DEFINED ONCE.

   Canonical here (cloud/api/console/public/components.css); a
   byte-identical copy is apps/desktop/src/components.css, and
   cloud/api/src/web/tokens.test.ts fails the suite when the two differ --
   the tokens.css mechanism, one layer up. Edit this one and re-copy.

   WHY IT EXISTS. On 2026-09-23 the operator asked for the desktop and the
   console to be "aligned with surgical precision", and the shared probe
   (packaging/audit-probe.js, `components`) measured 44 properties on
   which the two surfaces' SAME controls disagreed: the console's primary
   button had a 45% border and 16px sides where the desktop's had a solid
   one and 14px, its secondary button a grey fill and body ink where the
   desktop's was bare and muted, a small button 4px taller, pills a size
   up and fifty units bolder, chips a size down, fields 4px taller than
   the buttons beside them. Every one had been somebody's house style on
   one surface. Values that live in two files agree only until the next
   edit to one of them, so they live in one.

   WHAT IS HERE: the card, the segmented control, the three buttons, the
   pill and the chip -- controls both surfaces name by the SAME classes,
   or by one class each that this file maps together:
     primary    desktop .st-btn          console .btn.btn-primary
     secondary  desktop .st-btn.ghost    console .btn
     small      desktop .st-btn.sm       console .btn.btn-sm
   WHAT IS NOT: fields and table headers, which each surface selects in its
   own shape (classed on the desktop, by element on the console) and which
   carry identical VALUES instead, held by packaging/align-surfaces.mjs.

   ORDER. Loaded after tokens.css and BEFORE each surface's stylesheet, so
   every contextual rule a surface keeps (a topbar's control height, a
   danger variant, a page's own width) still wins exactly as it did.
   ===================================================================== */

/* ------------------------------------------------------------------ card
   The glass pairing: --card only reads as glass with the blur behind it.
   NO HOVER LIFT: a card is a readout, and a surface that rises under the
   pointer promises a click it cannot honour. Card-shaped BUTTONS declare
   their own hover. */
.card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: var(--pad-card);
  backdrop-filter: blur(var(--glass-blur));
  min-width: 0;
}

/* --------------------------------------------------- segmented control
   One shape for every "one of these" choice. The on state answers to
   every attribute the pages use, so no page has to change how it says it. */
.seg {
  display: inline-flex;
  gap: 3px;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-pill);
  padding: 3px;
  flex-wrap: wrap;
}

.seg-btn {
  background: transparent;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition:
    background-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}

.seg-btn:hover:not(:disabled) {
  color: var(--text);
}

.seg-btn.active,
.seg-btn.is-on,
.seg-btn[aria-pressed="true"],
.seg-btn[aria-selected="true"],
.seg-btn[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.seg-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

/* --------------------------------------------------------------- buttons
   The box every button shares. Control internals on the inset scale,
   sides on the layout scale -- which makes a button exactly as tall as a
   field (both inset-3 over the same type), so the two sit on one line. */
.st-btn,
.btn {
  border: 1px solid var(--card-border);
  border-radius: var(--radius-sm);
  padding: var(--inset-3) var(--space-4);
  font: inherit;
  font-size: var(--text-meta);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
  cursor: pointer;
  transition:
    filter var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}

/* Primary: the accent, edge and all. */
.st-btn,
.btn.btn-primary {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.st-btn:hover:not(:disabled),
.btn.btn-primary:hover:not(:disabled) {
  filter: brightness(1.15);
}

/* Secondary: no fill, the card's own edge, muted ink -- lit on hover.
   `:where()` keeps it at one class's weight, so the console's later
   variants still override it by order, as they did when this lived in
   app.css -- and a link-styled or danger button is not a secondary
   button, so neither takes this rule's hover. */
.st-btn.ghost,
.btn:where(:not(.btn-primary, .btn-link, .btn-danger)) {
  background: transparent;
  border-color: var(--card-border);
  color: var(--muted);
}

.st-btn.ghost:hover:not(:disabled),
.btn:where(:not(.btn-primary, .btn-link, .btn-danger)):hover:not(:disabled) {
  filter: none;
  color: var(--text);
  border-color: var(--accent);
}

/* Pressed: a shade darker for the frame the button is held, so a click
   registers under the pointer before whatever it started can show. */
.st-btn:active:not(:disabled),
.btn:active:not(:disabled) {
  filter: brightness(0.94);
}

.st-btn:disabled,
.btn:disabled {
  opacity: 0.5;
  cursor: default;
}

/* The compact size, for a button among table rows and figures. One
   modifier, so a small button is still the same button. */
.st-btn.sm,
.btn.btn-sm {
  font-size: var(--text-xs);
  padding: var(--inset-1) var(--inset-4);
}

/* ------------------------------------------------------------------ pill
   A state's name: uppercase, tracked, the smallest size. The status
   variants (colour + glyph + word) are each surface's own. */
.pill {
  display: inline-flex;
  align-items: center;
  padding: var(--inset-1) var(--inset-4);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-pill);
  background: var(--card);
  color: var(--muted);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ chip
   A readout's box: metadata, never a control. inline-flex so the border
   and the line grow to fit whatever container it lands in; the ellipsis
   so a long value is clipped inside its own box rather than the row's. */
.chip {
  display: inline-flex;
  align-items: center;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  color: var(--muted);
  font-size: var(--text-meta);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  min-width: 0;
}

/* ------------------------------------------------ the dropdown's popup
   The engine draws a <select>'s list from the element's own colours, and
   the dark card is a 5% glass over nothing -- so in dark the list came up
   in the engine's fallback. The select says which scheme it is, and the
   options take the opaque ground. (Desktop 2026-09-22; the console had
   the same defect and no fix until this file.) */
select {
  color-scheme: light;
}

[data-theme="dark"] select {
  color-scheme: dark;
}

select option,
select optgroup {
  background: var(--bg-soft);
  color: var(--text);
}

/* --------------------------------------------------------- WAYFINDING
   The four intents the desktop's rail is organised by -- Observe,
   Investigate, Measure, Instrument -- and the hue each carries
   (tokens.css, --nav-<group>, borrowed from the CVD-checked chart palette
   and measured per theme). Colour here means WHAT KIND OF THING THIS IS,
   the same way on every surface: never status, never data, never
   decoration. (2026-09-23: the operator asked for the console and the
   site to be colourised; the language already existed on the desktop, so
   it was shared rather than a second one invented.)

   Any element may name its intent with data-group; everything inside it
   reads --nav, --nav-soft and --nav-ink. Moved here from the desktop's
   styles.css, where it was scoped to main[data-group], so the three
   surfaces resolve a group's hue from one rule. */
[data-group="observe"] {
  --nav: var(--nav-observe);
  --nav-soft: var(--nav-observe-soft);
  --nav-ink: var(--nav-observe-ink);
}

[data-group="investigate"] {
  --nav: var(--nav-investigate);
  --nav-soft: var(--nav-investigate-soft);
  --nav-ink: var(--nav-investigate-ink);
}

[data-group="measure"] {
  --nav: var(--nav-measure);
  --nav-soft: var(--nav-measure-soft);
  --nav-ink: var(--nav-measure-ink);
}

[data-group="instrument"] {
  --nav: var(--nav-instrument);
  --nav-soft: var(--nav-instrument-soft);
  --nav-ink: var(--nav-instrument-ink);
}

/* THE KEY. A card that names its OWN intent -- on a page that mixes
   intents, like the console's dashboard, where the connection, the
   traces, the speed tests and the devices sit side by side -- shows it as
   a legend swatch before its title: the same square the charts use for a
   series, in the hue the desktop's rail uses for that kind of page. A
   card on a single-intent page carries no data-group of its own (the page
   does) and shows no key: repeating one colour on every card says nothing. */
.card[data-group] > h2::before,
.card[data-group] > .section-head h2::before,
.card[data-group] > .card-head h2::before {
  content: "";
  display: inline-block;
  width: 0.6em;
  height: 0.6em;
  margin-right: 0.55em;
  border-radius: calc(var(--radius-sm) / 4);
  background: var(--nav);
  vertical-align: 0.05em;
}
