/*
 * Shared look for the two configuration authoring surfaces: the Signal Builder (primary) and the advanced
 * Config Builder. They deliberately use the same cards, chips, hints and diagnostics so switching between them
 * feels like changing depth rather than changing tool.
 *
 * Global rather than scoped, because the classes are used by several components at once.
 */

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

.sb-root {
	display: flex;
	flex-direction: column;
	min-height: 0;
	flex-grow: 1;
}

.sb-body {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 340px;
	gap: 20px;
	flex-grow: 1;
	min-height: 0;
}

.sb-main {
	display: flex;
	flex-direction: column;
	min-width: 0;
	overflow: auto;
}

.sb-tabpanel {
	display: flex;
	flex-direction: column;
	gap: 14px;
	padding: 16px 4px;
}

@media (max-width: 1100px) {
	.sb-body {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ---- cards ---- */

.sb-card {
	border: 1px solid var(--neutral-stroke-rest);
	border-radius: var(--radius-lg);
	padding: 12px 14px;
}

/* ---- a card with a head ---------------------------------------------------------------------------
   The Options tab's two cards. A headed card can say in its strip what the body below works out — the
   retention plan and the size it settles at, the three things "Plant options" covers — so the card
   answers at a glance and the body is there to change, not to read. The padding moves off the card onto
   the two parts, which is why .sb-card-split zeroes it. */
.sb-card-split {
	padding: 0;
}

.sb-card-head {
	display: flex;
	align-items: baseline;
	gap: 10px;
	padding: 10px 14px;
	background: var(--neutral-fill-stealth-rest);
	border-bottom: 1px solid var(--neutral-stroke-divider-rest);
	border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
	min-width: 0;
}

.sb-card-title {
	font-size: var(--text-body2);
	font-weight: var(--fw-semibold);
	flex: 0 0 auto;
}

/* The recap. It gives way first — the title and the figure are the parts that must survive a narrow card. */
.sb-card-sub {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: var(--text-caption);
	color: var(--neutral-foreground-hint);
}

.sb-card-figure {
	flex: 0 0 auto;
	font-family: var(--font-mono);
	font-variant-numeric: tabular-nums;
	font-size: var(--text-caption);
	font-weight: var(--fw-medium);
	color: var(--color-accent);
}

.sb-card-body {
	display: flex;
	flex-direction: column;
	gap: 14px;
	padding: 12px 14px 14px;
}

/* One concern inside a headed card: a labelled hairline, then its controls. */
.sb-cluster {
	display: flex;
	flex-direction: column;
	gap: 8px;
	min-width: 0;
}

.sb-rule {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
}

/* The rule is the line the label sits on, drawn by the label's own row rather than by a border: that
   way whatever else the row carries — a segmented control — sits on the line too, not under it. */
.sb-rule::after {
	content: "";
	order: 1;
	flex: 1 1 auto;
	height: 1px;
	background: var(--neutral-stroke-divider-rest);
}

.sb-rule > :not(.sb-rule-label) {
	order: 2;
	flex: 0 0 auto;
}

.sb-rule-label,
.sb-colhead {
	font-size: var(--text-overline);
	font-weight: var(--fw-bold);
	letter-spacing: var(--ls-overline);
	text-transform: uppercase;
	white-space: nowrap;
}

.sb-rule-label {
	color: var(--hs-ink-2);
}

.sb-colhead {
	color: var(--hs-ink-4);
}

/* ---- the tier grid ---------------------------------------------------------------------------------
   Four columns, one row per tier: the name, the bucket size, the window, and the way out. Free-flowing
   rows put each tier's fields wherever the previous tier's happened to end, so nothing could be compared
   down the list — which is the whole reason a tier list is a list. */
.sb-tier-grid {
	display: grid;
	grid-template-columns: 96px max-content max-content 32px;
	column-gap: 12px;
	row-gap: 6px;
	align-items: center;
	min-width: 0;
}

.sb-tier-name {
	white-space: nowrap;
}

.sb-tier-fixed {
	font-size: var(--text-caption);
	font-style: italic;
	color: var(--neutral-foreground-hint);
}

.sb-tier-grid .sb-tier-drop {
	justify-self: start;
}

/* A number and the unit it is in are one value, so they are one control: the amount's right border is
   the dropdown's left. Two separate fields with a gap read as two settings. */
.sb-joined {
	display: flex;
	align-items: center;
	min-width: 0;
}

.sb-joined .sb-joined-num {
	width: 60px;
}

/* The border and the radius live on the input's root part (the class is on its fluent-field). */
.sb-joined .sb-joined-num > fluent-text-input::part(root) {
	border-top-right-radius: 0;
	border-bottom-right-radius: 0;
}

.sb-joined > .hs-select {
	margin-left: -1px;
}

/* Beats the .sb-root trigger rule further down, which rounds all four corners. */
.sb-root .sb-joined .hs-select-trigger {
	width: 116px;
	border-top-left-radius: 0;
	border-bottom-left-radius: 0;
	background: var(--neutral-fill-secondary-rest);
}

/* Adding a tier is a step out of the list, not another row in it: outlined, dashed, and only as wide as
   its words, so it never competes with the rows above. */
.sb-add-ghost {
	display: inline-flex;
	align-self: flex-start;
	align-items: center;
	gap: 6px;
	height: 28px;
	padding: 0 10px;
	border: 1px dashed var(--neutral-stroke-rest);
	border-radius: var(--radius-md);
	background: transparent;
	color: var(--neutral-foreground-rest);
	font: inherit;
	font-size: var(--text-caption);
	cursor: pointer;
}

.sb-add-ghost:hover {
	background: var(--neutral-fill-stealth-hover);
	border-style: solid;
}

.sb-add-ghost svg {
	width: 14px;
	height: 14px;
}

/* ---- the estimate's tiles and its two-position source ---- */

.sb-seg {
	display: inline-flex;
	padding: 2px;
	border: 1px solid var(--neutral-stroke-rest);
	border-radius: var(--radius-md);
	background: var(--neutral-fill-secondary-rest);
}

.sb-seg-btn {
	padding: 3px 10px;
	border: 0;
	border-radius: calc(var(--radius-md) - 2px);
	background: transparent;
	color: var(--neutral-foreground-hint);
	font: inherit;
	font-size: var(--text-caption);
	cursor: pointer;
}

.sb-seg-btn:disabled {
	color: var(--text-disabled);
	cursor: default;
}

.sb-seg-btn.on {
	background: var(--neutral-layer-1);
	box-shadow: var(--elevation-1);
	color: var(--color-accent);
	font-weight: var(--fw-semibold);
}

.sb-stats {
	display: flex;
	align-items: stretch;
	gap: 10px;
	flex-wrap: wrap;
}

.sb-stat {
	display: flex;
	flex-direction: column;
	gap: 2px;
	flex: 1 1 140px;
	min-width: 0;
	padding: 8px 10px;
	border-radius: var(--radius-md);
	background: var(--neutral-fill-stealth-rest);
}

.sb-stat-wide {
	flex: 1.6 1 220px;
}

.sb-stat-ok {
	background: color-mix(in srgb, var(--success) 12%, transparent);
}

.sb-stat-bad {
	background: color-mix(in srgb, var(--error) 12%, transparent);
}

.sb-stat-label {
	font-size: var(--text-overline);
	font-weight: var(--fw-bold);
	letter-spacing: var(--ls-overline);
	text-transform: uppercase;
	color: var(--neutral-foreground-hint);
}

.sb-stat-ok .sb-stat-label {
	color: color-mix(in srgb, var(--success) 70%, var(--neutral-foreground-rest));
}

.sb-stat-bad .sb-stat-label {
	color: color-mix(in srgb, var(--error) 70%, var(--neutral-foreground-rest));
}

.sb-stat-value {
	font-family: var(--font-mono);
	font-variant-numeric: tabular-nums;
	font-size: 19px;
	font-weight: var(--fw-medium);
	line-height: 1.2;
	color: var(--color-accent);
}

.sb-stat-note {
	font-size: var(--text-caption);
	line-height: var(--lh-body);
}

/* The three "why" panels. Closed they are a row of links; opened, whichever it is takes the row. */
.sb-disclosures {
	display: flex;
	flex-wrap: wrap;
	gap: 6px 18px;
	padding-top: 2px;
	min-width: 0;
}

.sb-disclosures > details {
	min-width: 0;
}

.sb-disclosures > details[open] {
	flex: 1 1 100%;
}

.sb-disclosures > details > summary {
	font-size: var(--text-caption);
	color: var(--neutral-foreground-hint);
	cursor: pointer;
	white-space: nowrap;
}

.sb-disclosures > details > summary:hover {
	color: var(--neutral-foreground-rest);
}

/* ---- one setting per row, in a headed card ---- */

.sb-optrows {
	gap: 0;
	padding-top: 4px;
	padding-bottom: 12px;
}

.sb-optrow {
	display: grid;
	grid-template-columns: 200px minmax(0, 1fr);
	column-gap: 20px;
	align-items: start;
	padding: 12px 0;
}

.sb-optrow + .sb-optrow {
	border-top: 1px solid var(--neutral-stroke-divider-rest);
}

.sb-optlabel {
	font-weight: var(--fw-semibold);
}

.sb-optfield {
	display: flex;
	flex-direction: column;
	gap: 4px;
	min-width: 0;
}

@media (max-width: 720px) {
	.sb-optrow {
		grid-template-columns: minmax(0, 1fr);
		row-gap: 6px;
	}
}

/* ---- groups ---- */

/* No overflow:hidden here — it clipped the device combobox's dropdown (the listbox is absolutely
   positioned inside the card). The head rounds its own corners instead to stay inside the card's radius. */
.sb-group {
	padding: 0;
}

/* ---- the two surfaces a row can take ----------------------------------------------------------------
   Hover and open are the only weight the rows carry, and both are a step in luminance rather than a
   fill: translucent ink that composites over whatever is behind, so one declaration serves a white card
   and a navy one. Mixing the row's own kind colour in was tried and fails in dark mode — navy at 6% into
   a navy card is not a step at all, and the row read as closed. The hue lives on the left edge, where it
   is a saturated line against both backgrounds. */
:root {
	--sb-lift-hover: rgba(12, 29, 56, .045);
	--sb-lift-open: rgba(12, 29, 56, .06);
	--sb-edge: rgba(12, 29, 56, .13);
}

body.theme-dark {
	--sb-lift-hover: rgba(255, 255, 255, .05);
	--sb-lift-open: rgba(255, 255, 255, .07);
	--sb-edge: rgba(255, 255, 255, .13);
}

/* The kind colours are all from the dark end of the navy/steel ramp, because they are chip fills that
   carry white text. As a 3px line on the dark-navy card they are all but invisible, so the accent is the
   kind mixed toward --sb-shade: no-op dark on a light card, lifted to a legible steel on a dark one. */
.sb-signal,
.sb-fold {
	--sb-accent-edge: color-mix(in srgb, var(--ident-bg, var(--grey-700)) 55%, var(--sb-shade));
}

.sb-group-head,
.sb-sig-head,
.sb-fold-head {
	display: flex;
	align-items: center;
	gap: 8px;
	min-width: 0;
	cursor: pointer;
	user-select: none;
}

/* The one thing on a head that may give way when the row runs out of width is the descriptive text, and
   it already truncates. Fluent's icons are plain <svg> flex items, so without this they shrink too — the
   16px chevron came out at 11px on a tight row, and since it leads the line, every chip and every name
   behind it moved with it. Five pixels of drift per row down a list of fifteen. */
.sb-group-head > svg,
.sb-sig-head > svg,
.sb-fold-head > svg {
	flex: 0 0 auto;
}

.sb-group-head {
	padding: 10px 12px;
	background: var(--neutral-fill-stealth-rest);
	border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
}

/* Collapsed group: the head is the card's only row, so its fill takes the bottom corners too. */
.sb-group-head:last-child {
	border-bottom-left-radius: calc(var(--radius-lg) - 1px);
	border-bottom-right-radius: calc(var(--radius-lg) - 1px);
}

/* The head already has a fill, so the hover step goes on as an image layer over it rather than replacing
   it — otherwise pointing at a group head swaps its colour instead of lifting it. */
.sb-group-head:hover {
	background-image: linear-gradient(var(--sb-lift-hover), var(--sb-lift-hover));
}

.sb-group-name {
	font-weight: var(--fw-semibold);
	font-size: var(--text-body1);
	color: var(--neutral-foreground-rest);
	white-space: nowrap;
}

.sb-group-body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 6px 12px 12px;
}

/* ---- one signal, folded to a line -------------------------------------------------------------------
   A collapsed signal is a line, not a card. Fifteen of them used to be fifteen bordered, shadowed boxes
   inside a bordered panel inside a bordered card — three edges deep before any content, and all that
   weight spent on rows that are one sentence long.

   Weight now goes only to the row that is open: it takes a surface and a 3px edge in its own kind
   colour, so "which one am I editing" is answered by the single thing on screen that has any body. The
   3px edge is reserved on every row (transparent when closed) so opening one doesn't shift its text. */
.sb-signal {
	border: 1px solid transparent;
	border-left-width: 3px;
	border-radius: var(--radius-md);
	background: transparent;
}

.sb-sig-head {
	padding: 5px 10px 5px 12px;
	min-height: 32px;
}

/* Only closed rows are targets: the open one already has a surface, and an implicit row can't be opened
   at all, so neither should light up under the cursor. */
.sb-signal:not(.sb-signal-open):not(.sb-signal-implicit):hover {
	background: var(--sb-lift-hover);
}

/* Open, the row is a small card: a shaded title bar carrying what the value *is*, and the settings on
   the plain surface below it. The lift used to cover the whole row, which left the identity controls
   and the provider settings on one continuous field — the two read as one long form once the identity
   controls gained labels of their own. Shading only the header separates them without a caption, a
   rule or a second colour. */
.sb-signal-open {
	margin: 4px 0;
	background: transparent;
	border-color: var(--sb-edge);
	border-left-color: var(--sb-accent-edge);
}

.sb-signal-open > .sb-sig-head,
.sb-signal-open > .sb-ident {
	background: var(--sb-lift-open);
}

.sb-signal-open > .sb-sig-head {
	border-radius: calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0 0;
}

/* ---- the identity band -----------------------------------------------------------------------------
   The editable counterpart of the head above it: for a signal its kind, name and unit; for a group its
   container, size and backing.

   It used to be a solid bar in the row's own chip colour. That did separate it from the settings, and it
   was by some distance the loudest thing on the page — a saturated navy strip carrying the least
   important content, with Fluent's light-chrome inputs pasted onto it. What actually needs to be true is
   only that identity and settings are not the same zone, and the head above already belongs to identity:
   so the band drops its fill, joins the head, and one hairline underneath splits the row in two.
   No caption, no colour, one line.

   Head replacement was tried first and rejected: expanding rewrote the very line that was clicked, so
   the row you were pointing at vanished under the cursor. Keeping the head and giving the controls
   their own band costs one line and holds the identity still. */
.sb-ident {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	padding: 2px 12px 12px;
	border-bottom: 1px solid var(--sb-edge);
	/* Slides down out of the head, so the controls read as having moved from the line above rather than
	   appearing from nowhere. The global prefers-reduced-motion guard in hydrasafe-design.css collapses
	   this to a near-zero duration, so no local media query is needed.

	   `backwards`, NOT `both`. `both` includes `forwards`, which leaves the last keyframe filled for the
	   life of the element — and a filled `transform: none` computes to the identity matrix, not to
	   `none`. Any transform other than `none` makes the element a stacking context, which trapped the
	   selects in this band: their listbox carries Fluent's z-index 9995, but a descendant cannot escape
	   its ancestor's stacking context, so the whole dropdown was confined to the band's own paint layer
	   and every positioned input further down the row — the formula field, the operand rows — drew over
	   it. `backwards` fills only *before* the animation starts, so the element ends on its own
	   `transform: none` and the band stops being a stacking context at all. */
	animation: sb-ident-in var(--dur-base) var(--ease-decelerate) backwards;
}

/* The group's own band takes the head's padding, so its first label starts under the head's chevron
   and on the body's content edge - it used to sit two pixels further in than either. */
.sb-group > .sb-ident {
	padding: 2px 12px 12px;
	background: var(--neutral-fill-stealth-rest);
}

@keyframes sb-ident-in {
	from {
		opacity: 0;
		transform: translateY(-6px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

/* The controls keep their set widths — a name field squeezed to nothing is worse than a taller band —
   so a narrow card wraps the band instead of shrinking its fields. */
.sb-ident > * {
	flex: 0 0 auto;
}

/* One custom cell range: its from/to fields travel as a unit when the band wraps, and the remove button
   sits on the input line (flex-end), not floating beside the taller labelled fields. */
.sb-cellspan {
	display: flex;
	align-items: flex-end;
	gap: 4px;
}

/* The add button joins the same input line as the range fields beside it. */
.sb-cellspan-add {
	display: flex;
	align-items: flex-end;
	align-self: flex-end;
}

/* Mapping errors take a full line under the fields — an overlap belongs to two rows at once, so it can't
   be pinned to either of them. */
.sb-cellspan-problems {
	flex-basis: 100%;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.sb-cellspan-problems > div {
	display: flex;
	align-items: center;
	gap: 6px;
}

/* The connective glyphs this band used to carry ("×" between container and count, "·" before the unit)
   are gone: every control states what it is now, so a separator between them was punctuation with
   nothing left to join. */

/* The row's verbs, at the foot of its settings. This was a filled strip of its own; on a row that is
   now only lightly tinted to begin with, a second surface inside it was one zone too many. Plain
   buttons at the bottom of the body say the same thing. */
.sb-sig-foot {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 4px;
	/* 2px on the left: a stealth button carries 10px of its own before its icon, so the icon lands on
	   the body's content edge, on the line the labels above it start on. */
	padding: 0 10px 8px 2px;
}

/* A locked row: a value the device or simulator creates without a signal being declared. It sits in the same
   list as the declared signals — "each item" must list everything that ends up in every item — but nothing
   on it can be edited. It used to say so with a dashed outline, which in a list of otherwise chrome-less
   rows is the loudest thing there. The padlock in the disclosure slot says it instead, and the name drops
   to the weight of something you can read but not touch. */
.sb-signal-implicit > .sb-sig-head {
	cursor: default;
}

.sb-signal-implicit .sb-sig-name {
	font-weight: var(--fw-medium);
	color: var(--neutral-foreground-hint);
}

/* The primary scan target in the whole editor: full ink, at body size. */
.sb-sig-name {
	font-weight: var(--fw-semibold);
	font-size: var(--text-body2);
	color: var(--neutral-foreground-rest);
	white-space: nowrap;
}

/* The "what it reads" line — a formula, register range or node id, so mono earns its place here. Recedes to
   secondary text, and truncates rather than wrapping so a row stays exactly one row. */
.sb-sig-detail {
	font-size: var(--text-caption);
	color: var(--neutral-foreground-hint);
	font-family: var(--font-mono);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}

/* Prose summaries (a backing's settings, "every 1000 ms · U₀ 1 V") are sentences, not code — mono made them
   look like data the user had to parse. */
.sb-summary {
	font-size: var(--text-caption);
	color: var(--neutral-foreground-hint);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}

.sb-sig-body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 12px;
}

/* A symbol binding: kept on the formula's own line instead of a stacked labelled block. */
.sb-operand {
	display: flex;
	align-items: center;
	gap: 4px;
	align-self: flex-end;
	padding: 2px 6px 2px 8px;
	border-radius: var(--radius-md);
	background: var(--neutral-fill-secondary-rest);
}

.sb-more {
	padding-top: 10px;
	border-top: 1px solid var(--sb-edge);
}

/* An alarm definition is its own zone, not another settings row: the dashed hairline it used to have
   disappeared between the formula above and the footer below. A soft strip of the warning tint says
   "this row is about alarming" — the definition of an alarm, not one firing, so it stays far below the
   saturation of a real alarm surface. --hs-warning-bg is the design system's own backing tint and has a
   dark-mode value, so no per-theme handling is needed here. */
.sb-alarm {
	/* 40%, measured: at 55% the guided 12px labels sat at 4.4:1 on the strip, just under AA. */
	background: color-mix(in srgb, var(--hs-warning-bg) 40%, var(--surface-card));
	border-radius: var(--radius-sm);
	padding: 6px 10px;
}

/* ---- the backing's tuning knobs -------------------------------------------------------------------
   Exactly the row a signal is: a line that carries a surface only while it is open. It used to be a
   permanently bordered box, which made the card teach two different things about what a fold looks
   like depending on whether it held settings or a signal. */

.sb-fold {
	border: 1px solid transparent;
	border-left-width: 3px;
	border-radius: var(--radius-md);
}

.sb-fold-head {
	display: flex;
	width: 100%;
	padding: 5px 10px 5px 12px;
	min-height: 32px;
	background: none;
	border: 0;
	color: inherit;
	font: inherit;
	text-align: left;
}

.sb-fold:not(.sb-fold-open):hover {
	background: var(--sb-lift-hover);
}

/* Same title-bar-and-body split as an open signal: the head is shaded, its settings are not. */
.sb-fold-open {
	background: transparent;
	border-color: var(--sb-edge);
	border-left-color: var(--sb-accent-edge);
}

.sb-fold-open > .sb-fold-head {
	background: var(--sb-lift-open);
	border-radius: calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0 0;
}

.sb-fold-title {
	font-size: var(--text-body2);
	font-weight: var(--fw-semibold);
	white-space: nowrap;
}

.sb-fold-body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 12px;
	border-top: 1px solid var(--sb-edge);
}

.sb-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
	align-items: flex-start;
}

.sb-list > .sb-signal {
	align-self: stretch;
}

/* The Add button under the rows: its icon on the rows' own text line. A row's chevron sits behind a
   3px edge and 12px of head padding, the button's icon behind 10px of its own. */
.sb-list > fluent-button {
	margin-left: 5px;
}

/* ---- a row of settings ---------------------------------------------------------------------------
   Labels sit beside their controls, not above. Stacking them read as "more air" rather than more order:
   it doubled the height of every fold and the eye still had to work out which label owned which control.

   What separates the fields is proximity, not space in general — the gap *between* two fields is three
   times the gap *inside* one. That single ratio is what turns a wrapping row of controls into a row of
   discrete label-value pairs, and it costs no height at all. Everything below serves that: the label
   recedes typographically, the value keeps full ink, and adjuncts stay tight against the field they
   qualify. */
.sb-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	column-gap: 26px;
	row-gap: 10px;
}

/* One labelled field, kept whole. Fluent renders a field's <label> as a *sibling* of its control, so
   both are flex items of .sb-row and a wrap can fall between them — leaving "Built by" on one line and
   its dropdown on the next. This pairs them into a single unbreakable item, and the tight inner gap is
   the near half of the proximity ratio above.

   Centered, not baseline-aligned: Fluent's controls are shadow-DOM hosts with no first baseline to
   align to, so `baseline` fell back per element type and the labels landed at visibly different
   heights across one row. Every control here is the same height, so its midline is the reliable line. */
.sb-field {
	display: flex;
	align-items: center;
	gap: 8px;
	min-width: 0;
}

/* The label recedes so the value reads as the answer. The field's label is the sibling
   label.hs-input-label (hydrasafe-design.css gives it Fluent UI 4's label look); the .sb-row / .sb-ident
   ancestor puts this treatment above that one. One treatment for both bands: the header is told apart
   by its surface, not by inventing a second label style for the same kind of thing. */
.sb-row .sb-field > label.hs-input-label,
.sb-ident .sb-field > label.hs-input-label,
.sb-field .hs-select-label,
.sb-adjunct .hs-select-label,
.sb-lead {
	font-size: var(--text-caption);
	font-weight: var(--fw-regular);
	color: var(--neutral-foreground-hint);
	line-height: var(--lh-tight);
	white-space: nowrap;
	margin: 0;
}

/* The app's dropdown stacks its label over its trigger. In the builder every label sits beside its
   control, so inside a field the two go on one line, at the field's inner gap, and the label takes the
   treatment above - one kind of label per row, whichever component draws it. Without this a row that
   mixed the two read at two heights: the dropdown was a two-storey item among one-storey fields, and
   its trigger hung below the midline the fields share. */
.sb-field > .hs-select,
.sb-adjunct > .hs-select {
	flex-direction: row;
	align-items: center;
	gap: 8px;
}

/* The trigger takes the builder's control size. Fluent's fields here are 32px with the design's large
   radius; the trigger is the toolbar chip's 30px and 8px, two pixels short of the field beside it on
   every row that has both. */
.sb-root .hs-select-trigger {
	height: 32px;
	border-radius: var(--radius-lg);
}

/* A field together with what qualifies it — the Scan buttons after an address, the checkbox after a poll
   rate, the symbols after a formula. This is one item of the row, so the wide between-fields gap falls
   outside it and the near gap inside; when the width runs out the adjunct wraps *under its own field*
   instead of drifting into the next one. (An earlier version used a negative margin to pull the adjunct
   closer, which of course leaked past the row's left edge the moment it wrapped.) */
.sb-fieldset {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	column-gap: 12px;
	row-gap: 8px;
	min-width: 0;
}

/* A connective between the identity band's controls ("16 items ·", "in") — quiet, never the point. */
.sb-head-x {
	font-size: var(--text-caption);
	color: var(--neutral-foreground-hint);
	white-space: nowrap;
}

/* Buttons, checkbox runs and notes that qualify the field before them rather than standing on their own. */
.sb-adjunct {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 12px;
	min-width: 0;
}

/* ---- the interior of a settings fold ------------------------------------------------------------
   A device fold ran connection, polling, measurement, alarm and access controls together in two long
   wrapping rows. Nothing said where one concern ended and the next began, so it read as a wall.

   The fix is grouping, not stacking: the controls keep their dense single-line rows, and a quiet name
   over each group says what the group is. One 11px line per group is the whole cost. */

.sb-clusters {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.sb-cluster {
	display: flex;
	flex-direction: column;
	gap: 5px;
	min-width: 0;
}

/* A hairline between clusters, not around them: boxing five clusters inside a fold inside a tray is
   exactly the nested-border stack this pass is removing. */
.sb-cluster + .sb-cluster {
	padding-top: 14px;
	border-top: 1px solid var(--sb-edge);
}

.sb-cluster-label {
	font-size: var(--text-overline);
	font-weight: var(--fw-bold);
	letter-spacing: var(--ls-overline);
	text-transform: uppercase;
	color: color-mix(in srgb, var(--neutral-foreground-hint) 60%, var(--neutral-foreground-rest));
	white-space: nowrap;
}

/* A fold's rows are short - a label and a field, twice - and they stack, so their fields line up in
   columns: every label in a fold takes one width and sets right, against its field, and the fields of
   one column share a width in the markup. Without this each row's first field started wherever its own
   label ended, and the simulator's six fields stood at five different x. */
.sb-fold-body .sb-field > label.hs-input-label,
.sb-fold-body .sb-field .hs-select-label,
.sb-fold-body .sb-lead {
	min-width: 108px;
	text-align: right;
}

.sb-adjunct .sb-hint {
	white-space: nowrap;
}

/* ---- the two value scopes -------------------------------------------------------------------------
   A group answers two questions about values — what every item gets, and what the group gets once — and
   they need to stay apart. They were panels: a tinted box with a more strongly tinted head, one azure,
   one violet. Two of those stacked on every group card, four groups down the page, and the strongest
   rhythm on the screen became an alternating blue-and-pink stripe that carried no information. Filled
   pastel blocks also read as callouts — the shape the app uses for warnings — so the structure looked
   like a run of notices.

   A section is a label and a rule now. The hue survives in the one place it still does work: the label's
   own ink, and a 2px guide down the rows that belong to it. Same distinction, no boxes, and the card
   goes from four nested edges to one.

   Nothing here uses --hs-error/-warning/-success: those stay reserved for status.

   No overflow:hidden anywhere in this tree — the signal editors and the backing fold contain comboboxes
   whose listboxes are absolutely positioned and have to escape (see .sb-group). */

/* What a bright hue gets mixed with to make ink out of it: darkened for light surfaces, lightened for
   dark ones. Both values must be tokens defined at or above <body> — a var(--sb-hue) here would be
   resolved against <body>, where --sb-hue does not exist, and a custom property whose value fails to
   substitute is invalid at computed-value time and inherits down as invalid, silently taking every mix
   that reads it with it (the rails came out --neutral-stroke-strong-rest grey). */
:root {
	--sb-shade: var(--navy-900);
}

body.theme-dark {
	--sb-shade: var(--grey-050);
}

.sb-section + .sb-section {
	margin-top: 14px;
}

/* Two hues from *inside* the approved #27F5E0→#F527C5 range, not its endpoints — the raw teal and
   magenta read as neon against the navy app. The path between them runs through blue and violet, so
   per-item takes azure (~210°) and group takes violet (~275°): calmer, clearly apart, and the blue
   sits naturally beside the brand navy. Shaded to ink: the raw hues are 3.0:1 and 4.4:1 on white, the
   mixes 5.0:1 and 6.5:1, and in dark mode the same declaration lightens instead. */
.sb-section-item {
	--sb-hue: #2782F5;
}

.sb-section-group {
	--sb-hue: #9A27F5;
}

.sb-section {
	--sb-accent: color-mix(in srgb, var(--sb-hue, var(--neutral-foreground-hint)) 78%, var(--sb-shade));
}

/* Label, count, then a hairline out to the card edge — the section marker that costs one line and no
   box. The rule is a pseudo-element so it always takes exactly the width the text leaves. */
.sb-section-head {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
	padding: 0 0 4px;
}

/* Tinted with the section's own hue rather than left neutral: at the divider grey the rule was invisible
   against the card, and a neutral line strong enough to see would have read as another box edge. */
.sb-section-head::after {
	content: "";
	flex: 1 1 auto;
	height: 1px;
	background: color-mix(in srgb, var(--sb-hue) 30%, transparent);
}

/* An overline, not a heading. At body size and 700 it outranked the group name above it — a subsection
   shouting louder than the card it is inside. The hue is what makes it findable now, not the size. */
.sb-section-title {
	font-size: var(--text-overline);
	font-weight: var(--fw-bold);
	letter-spacing: var(--ls-overline);
	text-transform: uppercase;
	color: var(--sb-accent);
	white-space: nowrap;
}

/* "3 × 16 = 48" — the arithmetic the section implies, stated where the rows that get multiplied are. */
.sb-section-note {
	font-size: var(--text-caption);
	color: var(--neutral-foreground-hint);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}

/* The guide binds the rows to their label without enclosing them: pale, half the weight of the card's
   backing border and well inside it, so the two read as indentation rather than as the parallel-rails
   stack the panels used to produce. The 12px inset is what keeps it clear of the 3px accent an open row
   puts on its own left edge — at 8px the two lines were close enough to read as one double rail.
   It starts on the group body's content edge, the line the backing fold's own left edge is on: two
   pixels in, it ran beside the fold's navy accent, a second rail offset by exactly that much. */
.sb-section-body {
	margin-left: 0;
	padding: 2px 0 0 12px;
	border-left: 2px solid color-mix(in srgb, var(--sb-hue) 22%, transparent);
}

/* Hint text sitting beside a field rather than under it. */
.sb-inline {
	align-self: center;
	max-width: 420px;
}

/* ---- discovered observers in the connection field's suggestions ---- */

/* The chevron at the end of a free-text field that opens its suggestions: a glyph in the field, as the
   dropdown button of a combobox was, not a button of its own. */
fluent-button.sb-suggest {
	background-color: transparent;
	width: 24px;
	height: 24px;
	min-height: 0;
	padding: 0;
	margin-right: -4px;
}

/* The list is as wide as its longest entry, which keeps the link-local IPv6 addresses whole. */
.sb-suggest-menu fluent-menu-list {
	width: max-content;
	max-width: min(480px, 90vw);
}

/* The observer head is a disabled item so it scrolls with its addresses but can't be picked.
   Outer document rules beat the component's :host([disabled]) dimming, so it reads as a group
   label rather than a greyed-out entry. */
fluent-menu-item.sb-observer-head {
	opacity: 1;
	cursor: default;
	font-size: var(--text-caption);
	font-weight: var(--fw-semibold);
	color: var(--neutral-foreground-hint);
}

fluent-menu-item.sb-observer-head:not(:first-of-type) {
	margin-top: 2px;
	border-top: 1px solid var(--neutral-stroke-divider-rest);
}

/* The selectable addresses, indented under their observer. Mono, like every other address the
   builder shows. */
fluent-menu-item.sb-observer-addr {
	padding-left: 24px;
	font-family: var(--font-mono);
}

/* ---- text ---- */

.sb-hint {
	font-size: var(--text-caption);
	line-height: var(--lh-body);
	color: var(--neutral-foreground-hint);
}

.sb-hint code,
.sb-signal code {
	font-family: var(--font-mono);
	font-size: .95em;
	padding: 0 3px;
	border-radius: var(--radius-sm);
	background: var(--neutral-fill-secondary-rest);
}

.sb-warn {
	font-size: var(--text-caption);
	color: var(--warning);
}

/* ---- chips ---- */

/* Chips and badges are pill-shaped per the design system; only cards/inputs take the 9px radius.
   Solid-filled so the kind is actually readable — a 14% tint was too washed out to register. It stays
   subordinate to the signal name by being small (overline size) and tight rather than by being pale.
   The colour arrives as --chip (see Palette). */
.sb-chip {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
	flex: 0 0 auto;
	font-size: var(--text-overline);
	font-weight: var(--fw-semibold);
	padding: 2px 8px;
	border-radius: var(--radius-pill);
	white-space: nowrap;
	align-self: center;
	letter-spacing: .02em;
	color: #FFFFFF;
	background: var(--chip, var(--navy-700));
}

/* The kind chip opens every row in the list, and the chips are all different lengths — "Fixed" against
   "Aggregate" — so the names behind them used to start at eight different x positions down one list.
   A fixed column puts every signal name on the same line of scan. Sized to the longest label. */
.sb-chip-kind {
	flex: 0 0 auto;
	min-width: 76px;
}

.sb-chip-ghost {
	color: var(--neutral-foreground-hint);
	background: transparent;
	border: 1px solid var(--neutral-stroke-rest);
	font-weight: var(--fw-medium);
}

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

.sb-thresholds,
.sb-scales {
	border-collapse: collapse;
	margin-top: 8px;
	font-size: .9em;
}

.sb-thresholds th,
.sb-scales th {
	text-align: left;
	font-size: .8em;
	color: var(--neutral-foreground-hint);
	font-weight: 600;
	padding: 2px 10px 4px 0;
}

.sb-thresholds td,
.sb-scales td {
	padding: 2px 10px 2px 0;
}

.sb-thresholds th:not(:first-child),
.sb-thresholds td:not(:first-child) {
	text-align: center;
	padding-right: 16px;
}

/* ---- preview rail ---- */

.sb-preview {
	display: flex;
	flex-direction: column;
	gap: 12px;
	overflow: auto;
	background: var(--neutral-fill-stealth-rest);
	border-radius: var(--radius-lg);
	padding: 14px;
	align-content: start;
}

.sb-preview-title {
	font-weight: 600;
	display: flex;
	align-items: center;
	gap: 6px;
}

.sb-tree {
	font-family: var(--font-mono);
	font-size: var(--text-caption);
	line-height: 1.7;
}

.sb-diag {
	display: flex;
	gap: 8px;
	align-items: flex-start;
	padding: 6px 8px;
	border-radius: var(--radius-md);
	font-size: var(--text-caption);
	line-height: var(--lh-body);
}

.sb-diag-error {
	background: color-mix(in srgb, var(--error) 12%, transparent);
}

.sb-diag-warn {
	background: color-mix(in srgb, var(--warning) 12%, transparent);
}

.sb-diag-ok {
	background: color-mix(in srgb, var(--success) 12%, transparent);
}

/* ---- starter cards ---- */

.sb-starters {
	display: flex;
	flex-direction: column;
	gap: 14px;
	max-width: 900px;
}

.sb-starter-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
	gap: 12px;
}

.sb-starter {
	display: flex;
	gap: 12px;
	align-items: flex-start;
	text-align: left;
	padding: 14px;
	border: 1px solid var(--neutral-stroke-rest);
	border-radius: var(--radius-lg);
	background: var(--neutral-layer-1);
	color: inherit;
	font: inherit;
	cursor: pointer;
	transition: border-color var(--dur-base) var(--ease-standard), box-shadow var(--dur-base) var(--ease-standard);
}

.sb-starter:hover {
	border-color: var(--accent-fill-rest);
	box-shadow: var(--elevation-2);
}

.sb-starter-title {
	font-weight: 600;
	margin-bottom: 2px;
	display: flex;
	gap: 8px;
	align-items: center;
}

/* ---- advanced Config Builder ---- */

.cfg-card {
	border: 1px solid var(--neutral-stroke-rest);
	border-radius: var(--radius-lg);
	overflow: hidden;
	transition: box-shadow var(--dur-base) var(--ease-standard);
}

.cfg-card:hover {
	box-shadow: var(--elevation-2);
}

.cfg-card-header {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 9px 12px;
	background: var(--neutral-fill-stealth-rest);
}

.cfg-card-toggle {
	display: flex;
	align-items: center;
	gap: 10px;
	flex: 1;
	min-width: 0;
	cursor: pointer;
	user-select: none;
}

.cfg-name {
	font-weight: 600;
	white-space: nowrap;
}

.cfg-summary {
	color: var(--neutral-foreground-hint);
	font-size: .85em;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.cfg-banner {
	display: flex;
	gap: 12px;
	align-items: center;
	padding: 10px 14px;
	border-radius: var(--radius-lg);
}

/* FluentCard clips its content by default, which cuts off any open dropdown inside a builder card. */
.sb-root .fluent-card,
.cfg-card {
	contain: inherit !important;
	content-visibility: inherit;
}
