ui(settings): restructure Normalization section for clarity and breathing room

The mode picker (Off / ReplayGain / LUFS) used to live in the right-hand
action slot of a settings-toggle-row and the per-mode controls were
stacked into the same parent with footer-style help text. Hard to scan,
visually cramped, and odd compared to the rest of the Settings page.

Refactor:

- Mode picker becomes a full-width segmented row with even-flex buttons,
  using the new .settings-segmented utility.
- Each mode renders its own .settings-norm-block sub-section with a
  subtle accent tint and border so the active configuration reads as
  one coherent group.
- Inside the block, every setting is its own .settings-norm-field
  (control row + per-control help text immediately below). 1.1 rem gap
  between fields, 0.45 rem between row and help — clearly groups
  related text without crowding.
- Sliders no longer max-cap at 200 px and instead flex to fill the row.
- Inactive ghost buttons (Off, ReplayGain, RG mode, LUFS targets) get a
  visible border and a faint surface tint so they read as selectable
  slots in dark themes too.
- LUFS mode gets a dedicated note-box explaining that brief volume drift
  on the very first play of a new track is the analysis pass at work,
  not a bug — subsequent plays use the cached measurement, and queued
  tracks are usually pre-analysed during the previous song.
- "Trim before measurement (dB)" renamed to "Pre-analysis attenuation"
  (and equivalents in 8 locales).
- New i18n keys: normalizationDesc, normalizationOff/ReplayGain/Lufs,
  loudnessTargetLufsDesc, loudnessFirstPlayNote, replayGainPreGainDesc,
  replayGainFallbackDesc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-26 03:10:47 +02:00
parent d467ea1779
commit 756b189bcc
10 changed files with 314 additions and 146 deletions
+98
View File
@@ -3707,6 +3707,104 @@
background: color-mix(in srgb, var(--accent) 8%, transparent);
}
/* ─ Normalization block (per-mode sub-section) ─ */
.settings-segmented {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.settings-segmented > .btn {
flex: 1 1 0;
min-width: 0;
font-size: 13px;
padding: 8px 16px;
}
.settings-segmented > .btn-ghost {
border: 1px solid var(--border-subtle);
background: color-mix(in srgb, var(--text-primary) 4%, transparent);
}
.settings-segmented > .btn-ghost:hover {
border-color: color-mix(in srgb, var(--accent) 40%, var(--border-subtle));
background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.settings-norm-block .btn-ghost {
border: 1px solid var(--border-subtle);
background: color-mix(in srgb, var(--text-primary) 4%, transparent);
}
.settings-norm-block .btn-ghost:hover {
border-color: color-mix(in srgb, var(--accent) 40%, var(--border-subtle));
background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.settings-norm-block {
display: flex;
flex-direction: column;
gap: 1.1rem;
padding: 1.2rem 1.25rem;
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--accent) 4%, transparent);
}
.settings-norm-field {
display: flex;
flex-direction: column;
gap: 0.45rem;
}
.settings-norm-row {
display: flex;
align-items: center;
gap: 0.9rem;
flex-wrap: wrap;
}
.settings-norm-row > input[type="range"] {
flex: 1;
min-width: 120px;
}
.settings-norm-label {
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
min-width: 160px;
}
.settings-norm-value {
font-size: 12px;
color: var(--text-muted);
min-width: 56px;
text-align: right;
font-variant-numeric: tabular-nums;
}
.settings-norm-help {
font-size: 12px;
color: var(--text-muted);
line-height: 1.5;
max-width: 720px;
}
.settings-norm-note {
font-size: 12px;
color: var(--text-secondary);
line-height: 1.5;
padding: 0.75rem 0.9rem;
border-left: 2px solid var(--accent);
background: color-mix(in srgb, var(--accent) 7%, transparent);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
margin-top: 0.3rem;
}
@media (max-width: 640px) {
.settings-norm-label {
min-width: 0;
flex-basis: 100%;
}
}
.settings-section-divider {
border: none;
border-top: 1px solid var(--border);