/* SettingsFieldRow (U8 / KTD-10) — base layout for a single settings control: * label + optional scope badge on the leading line, the control slot beside or * below it, then help text and an error band. Shared by every typed row so the * redesigned SettingsModal and the WorkflowSettingsPanel read identically. * Mirrors the token/class conventions of WorkflowFieldsPanel.css. */ /* FNXC:SettingsStyling 2026-07-15-17:35: Every settings row must render from one type scale, because the pre-migration modal styled a text field's label and a toggle's label three different ways inside the same section: `.form-group label` (12px/600/uppercase/muted), a `.settings-content` override narrowing that to 0.72rem, and `.checkbox-label` (13px/500/sentence-case) whose declarations each carried `!important` purely to out-specify the first two. Sizes name shared `--font-size-*` rungs rather than raw values so the scale stays auditable: label = sm, help/error = xs, scope badge = 2xs. Sentence-case at weight 500 is the surviving idiom; the uppercase/muted treatment is dropped because a settings label names its control rather than heading a column. Rows carry no horizontal padding — `.settings-content` already owns the inset, and a second one would double-indent every row. */ .settings-field-row { display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-sm) 0; scroll-margin-block: var(--space-xl); } .settings-field-row.is-disabled { opacity: 0.6; } /* FNXC:SettingsStyling 2026-07-15-17:35: Inline rows (booleans) sit the control on the label's line. The help text then hangs beneath BOTH, indented past the control so it aligns with the label it explains rather than with the checkbox — without the indent the copy starts under the box and the column of help text no longer lines up with the column of labels. The indent is derived from the control's own width plus the head gap, so it tracks the checkbox rather than being a magic number. */ .settings-field-row--inline { --settings-inline-control-width: 13px; } .settings-field-row--inline .settings-field-row-head { gap: var(--space-sm); } .settings-field-row--inline .settings-field-row-label { cursor: pointer; } .settings-field-row--inline .settings-field-row-error { padding-inline-start: calc(var(--settings-inline-control-width) + var(--space-sm)); } /* FNXC:SettingsSearch 2026-07-15-17:35: Jump-to-field: choosing a search result scrolls that row into view and flags it here, so the eye lands on the right control inside a long section instead of on the section's top. The hold is deliberately longer than any `--duration-*` UI-transition token (those top out at 0.3s, which reads as a flicker on a row the operator is still hunting for), so the timing is a named local property rather than a borrowed token. It must stay a duration-only value: `--transition-*` tokens bundle an easing keyword, and substituting one beside `ease-out` yields two easing functions, which invalidates the declaration and silently resolves to `animation: none` (the failure that froze 14 spinners in FN-5855). The wash paints background and box-shadow only — never a border or padding — so a landing row does not reflow its neighbours by a pixel. `scroll-margin-block` keeps the landed row clear of the section chrome rather than flush beneath it. */ .settings-field-row { --settings-search-match-duration: 1.6s; --settings-search-match-tint: color-mix(in srgb, var(--accent, #7c5cbf) 18%, transparent); } .settings-field-row.is-search-match { border-radius: var(--radius-sm); animation: settings-field-row-match var(--settings-search-match-duration) ease-out; } @keyframes settings-field-row-match { 0%, 60% { background: var(--settings-search-match-tint); box-shadow: 0 0 0 var(--space-xs) var(--settings-search-match-tint); } 100% { background: transparent; box-shadow: 0 0 0 var(--space-xs) transparent; } } /* FNXC:SettingsStyling 2026-07-15-17:35: A reduced-motion operator still needs to see which row matched, so the wash resolves to a static hold instead of being animated away entirely. */ @media (prefers-reduced-motion: reduce) { .settings-field-row.is-search-match { animation: none; background: var(--settings-search-match-tint); } } /* FNXC:SettingsStyling 2026-07-16-00:10: `align-items: flex-start` (not center) so a checkbox sits on the label's FIRST line. Centering aligns it to the midpoint of a label that has wrapped to two or three lines, which floats the box down beside the middle of the sentence. `flex-wrap: nowrap` because the head now holds at most two items — the control and the label group — and the group is what absorbs the wrapping. Allowing the row itself to wrap is what stranded the checkbox on its own line. */ .settings-field-row-head { display: flex; align-items: flex-start; gap: var(--space-xs); flex-wrap: nowrap; } /* FNXC:SettingsStyling 2026-07-16-00:10: Label + scope badge + help tip flow as one run of prose: `min-width: 0` lets the group shrink below its content's intrinsic width (a flex item refuses to by default, which is what forces the wrap), and `flex: 1 1 auto` gives it the width left over beside the control. The children stay inline, so the badge and the "?" trail the label's LAST word rather than being pushed onto their own line — and a wrapped second line starts under the first word, not under the checkbox. */ .settings-field-row-labelgroup { flex: 1 1 auto; min-width: 0; display: block; } /* FNXC:SettingsStyling 2026-07-16-00:10: The group's children are inline, so the head's flex `gap` no longer separates them — it only ever applied between flex items, and the label/badge/tip are now text runs inside one item. Their spacing has to come from margins instead, or the badge renders flush against the label ("Open tasks in the right sidebarPROJECT"). Margin-inline-start rather than a gap so the spacing travels with each trailing element when the label wraps: the badge stays attached to the last word rather than to a column edge. */ .settings-field-row-labelgroup > * { vertical-align: middle; } .settings-field-row-labelgroup > .settings-field-row-scope, .settings-field-row-labelgroup > .settings-help { margin-inline-start: var(--space-xs); } .settings-field-row-label { font-size: var(--font-size-sm); font-weight: 500; line-height: var(--line-height-tight); color: var(--text); } .settings-field-row-scope { font-size: var(--font-size-2xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); background: var(--surface-muted, color-mix(in srgb, #ffffff 4%, transparent)); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 1px 6px; } .settings-field-row-scope--global { color: var(--accent, #7c5cbf); border-color: var(--accent, #7c5cbf); } .settings-field-row-control { display: flex; align-items: center; gap: var(--space-xs); } /* FNXC:SettingsStyling 2026-07-15-17:35: Controls hold the 38px min-height the pre-migration `.settings-content` rule gave inputs, so a migrated row keeps its touch target and does not visibly shrink beside a not-yet-migrated one during the rollout. */ .settings-field-row-control > input:not([type="checkbox"]), .settings-field-row-control > select, .settings-field-row-control > textarea { flex: 1; min-width: 0; min-height: 38px; box-sizing: border-box; } .settings-field-row-clear { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; background: none; border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text-muted); cursor: pointer; padding: 2px; transition: color var(--duration-fast) ease, border-color var(--duration-fast) ease; } .settings-field-row-clear:hover:not(:disabled) { color: var(--text); border-color: var(--text-muted); } .settings-field-row-clear:disabled { cursor: default; opacity: 0.5; } .settings-field-row-error { margin: 0; font-size: var(--font-size-xs); line-height: 1.5; color: var(--color-error, #f85149); }