The two reorder helpers were 21-line near-duplicates that drifted apart
twice during W8c before being realigned. Extracted the shared swap
into a generic reorderLadder<T>() helper so both callers reduce to
read-ladder, call helper, write back. Closes W10 simplifier S3.
moveDeviceType now takes bucket: string | null where null means the
global ladder. The function maps null to the canonical "default" key
once at the top of the body and reads + writes that key uniformly,
removing the two equal-shape if branches and the magic-string check
that drove them. Callers pass null instead of "__default__".
Extends the W8.3 44px touch-target rule and the W8e accordion ARIA
contract to surfaces that shipped without them:
- Insights tab-strip pills: pills are primary navigation; promote to
`min-h-11` so a thumb tap on "Stimmung" cannot bleed into the
neighbour two pixels below.
- Injection-site picker: raise the invisible hit-target radius from
11 to 12 SVG units so the rendered diameter (\~25.6 CSS px)
clears the WCAG 2.5.8 Level AA 24×24 floor. The abdomen left/right
pair at \Δx=24 caps the upper bound — larger circles would
collide.
- Source-priority reorder arrows (per-metric + device-type axes):
promote four `h-7 w-7` icon buttons to `h-11 w-11`.
- Sub-page heading: replace the bare `focus-visible:outline-none`
with the same ring vocabulary used by insights pills + the Coach
drawer so sighted-keyboard users get a visible indicator on
programmatic focus.
- Source-priority per-metric + device-type accordions: pair
`aria-expanded` with `aria-controls` + matching `id` on the
panel, mirroring the W8e Provenance contract.
- Maintainership banner dismiss button: 22×22 hit zone replaced by a
44×44 inline-flex button that keeps the same icon size.
- Injection-site picker group: `aria-label` was leaking the
cadence-preset string; introduce
`medications.injectionSitePickerAriaLabel` and translate it
across all six locales.
- Body-outline SVG: hardcoded English "Body outline" replaced by
`medications.injectionSiteBodyOutlineAriaLabel` with native
translations in DE/FR/IT/ES/PL.
Extend Settings → Sources with two collapsed-by-default expanders below
the existing global ladder (Marc no-split directive — one screen, three
vertically-stacked sections):
1. Per-metric overrides — surfaces every metric whose ladder has been
edited away from the default; each row reveals a one-click reset
so a single metric can roll back without touching the rest.
2. Device-type priority — the new W8c axis. Default ladder uses the
open-wearables-compatible 'watch beats phone beats scale' order;
up/down arrows + 'Reset device-types' keep the same interaction
vocabulary as the existing source ladder.
Save round-trips the W8c canonical shape (metricPriority +
deviceTypePriority); the server's parseSourcePriority already echoes
the resolved shape back so the optimistic update is a setQueryData.
Reset PUTs an empty body so the server returns constant defaults.
i18n: 11 new keys under settings.sections.sources.* (en + de). German
prefers Umlaute (Uhr, Geräte-Typ-Reihenfolge, individuelle Anpassungen).
Tests: settings-shell.test.tsx already gates the navigation
ordering — keeps passing (12/12). No new component tests yet; the
integration coverage in 8c.5 exercises the new shape end-to-end.
7 conflict files resolved by taking the upstream/HEAD side (which carries
the W4d/W5/W6c/W3f feature additions). Stashed-side changes that were
genuine new features re-land in subsequent commits if any were lost.
This sweeps up the loose state the W4e+W5e+W5d combined agent left
mid-stash. Resolves develop's working tree for clean push.