# Spectrum Controls — Visual Rework The strip between the spectrum and the radio controls (`#spectrum-controls`) holds eight controls in two groups. This proposes how it should *look*. Every control stays, in its current order, with its current name and its current behaviour. Nothing here changes what a button does, what commits when, or what is stored. It is a styling and layout change. *Status: implemented. Kept as the record of what was changed and why.* --- ## What it looks like now ``` Bandwidth [ 12 ] kHz [Set] [Auto BW] [Sweet-spot] Peak Hold [2 s] Floor [-115] dB Range [90] dB [Auto] Contrast [——●——] 1.0 ``` Four problems, all of them visual: **1. Four different control heights on one line.** The bare number inputs, the buttons, the `select` and the range slider are each sized by their own rule, so nothing shares a baseline and the row reads as a pile rather than a strip. **2. Units are loose text.** `kHz`, `dB`, `dB` and the contrast value `1.0` are text nodes sitting outside the control they belong to, separated from it by a gap the same size as the gap between unrelated controls. The eye has to work out which number owns which unit. **3. A quarter of the strip is a hole.** `justify-content: space-between` puts about 250 px of nothing in the middle at 1600 px, and the two groups read as two unrelated things because the only thing between them is emptiness. **4. The groups stagger between 1100 and 1400 px.** The bandwidth group wraps to two lines while the level group stays on one, so the level group floats at a height of its own, aligned with neither line of the group beside it. This is the worst of it, and it happens at a common window width. Two smaller things: 24 px controls are below any touch-target guideline, and the contrast readout has no fixed width, so the row twitches as the value changes between `1.0` and `0.9`. ## Proposed **One control height, units inside their field, and a rule where the clusters meet.** - **A field is one box.** Label, value and unit share a single bordered box — `Bandwidth │ 12.0 │ kHz` — so a number and its unit can never be read apart. Fields, buttons, the peak-hold select and the contrast slider are all 1.7 rem tall, on 44 px targets under a coarse pointer. - **A rule, not a hole.** The two clusters are separated by a thin vertical rule with normal spacing either side. The slack goes to a flexible spacer, so the strip is left-aligned rather than pushed apart. - **A cluster never splits.** Each cluster is `nowrap`; the container wraps. If a cluster does not fit on the line it drops whole to the next one, left-aligned with the one above. No staggering, at any width. - **Rules fall away at line starts.** A cluster that begins a line has no rule hanging off its left edge. - **The contrast readout gets a fixed, tabular slot**, so the row is still. Below the existing mobile breakpoint the strip already stacks; the same field component applies there, which is most of what makes it look deliberate. ## What this does not change `Set` stays. `Auto BW` and `Auto` keep their names, even though they mean different things — that is a naming question, not a styling one. Sweet-spot stays where it is and keeps its behaviour. Nothing gains or loses persistence. Nothing moves into a popover, and no control is hidden behind a click. Those are all worth arguing about separately; a note of them is at the end of this file so the arguments are not lost. ## Implementation One pass, no behaviour touched: 1. `.spectrum-field` and `.spectrum-btn` in `style.css`, replacing the six per-id rules (`#spectrum-bw-input`, `#spectrum-floor-input`, `#spectrum-range-input`, `#spectrum-bw-label`, `#spectrum-floor-label`, `#spectrum-range-label`) that currently repeat the same declarations. 2. Markup in `index.html`: the loose `kHz` / `dB` text nodes move inside their label, which keeps every id and every event handler exactly where it is. 3. `#spectrum-controls` becomes a wrapping flex row with a spacer; `#spectrum-bw-row` and `#spectrum-level-row` become `nowrap` clusters with a left rule. 4. The mobile block in the media query drops the rules it no longer needs. `app.ts` is not touched. Every id survives, so the existing handlers, the `spectrum-layout.mjs` geometry test and the broadcast-layout highlight all keep working. ### Tests Extend `spectrum-layout.mjs`, which already measures this area: - Every control in the strip shares one height, at 1600, 1200 and 900 px. - No two clusters sit at different vertical offsets on the same line — the staggering bug, asserted directly. - The strip never overflows its container and never overlaps the hint line. --- ## Noted for later, not proposed here Behavioural observations from reading the code, kept so they are not lost: - `Auto BW` (filter) and `Auto` (display scaling) are both called Auto, 600 px apart. - `Floor`, `Range` and `Contrast` are not persisted; `Peak Hold` is. - `Auto` and `Auto BW` are one-shot: no state, nothing to turn off. - `Sweet-spot` retunes the SDR and waits up to 1.4 s per candidate centre, with no busy indication. - Contrast resets on a double-click that nothing advertises.