Reported: the ticker was painting OVER the socials. The coupling rule existed and was tested
(ScoreboardLayout.ResolveEdges) but nothing ever CALLED it — the strip's Y still came from its own
preference, so both bars landed on the south edge.
Now the resolution runs at publish time: the social bar keeps its side, the scoreboard takes the
other one, and its Y follows from that edge. The pair re-sorts whenever the social bar moves (the
preview click-toggle) or shows/hides, because the rule is "resolve from what is visible", not a
stored pairing.
The layer also gains an Edge preference (Top/Bottom) in its properties, labelled "socials take the
other" — a preference, never a claim on an occupied edge.
Also stabilised the opacity assertion: it samples a plate-only pixel 5px from the left edge instead
of the first semi-transparent pixel it finds (which could be anti-aliased text).
Four reports, three causes:
1. "Background plate has no code behind it" — the plate COLOUR and OPACITY controls were disabled
until the plate switch was on, which reads as dead controls. They are always settable now; the
switch is what decides whether a plate is drawn.
2. "Opacity doesn't work" — proven in the renderer test: 20% and 80% produce alphas of ~51 and ~204,
so the slider reaches the pixels. What was actually wrong was the box.
3. The strip's BOX was fixed at 56px while a wrapped strip renders taller — the frame was scaled
into the box and looked squashed/clipped. The source's width/height now follow the rendered
frame, and its Y follows from the edge.
4. "Simulate (dev) should override Show the scoreboard" — it does: the preview gate is
(_scoreboardEnabled || _scoreboardSimulate), so the dev toggle works even with the product
setting off.
Not changed: the strip is STATIC by design (all enabled facts always visible, wrap rather than
scroll). If scrolling is wanted after all, that is a presentation mode to add, not a fix.
Rasterises enabled tiles into one straight-alpha BGRA frame: measured widths -> FitLine (shrink,
then wrap, never scroll) -> centred lines with dim separators, over an optional rounded plate whose
colour and opacity are the creator's knobs. Uses OutputPalette.Sans (bundled Inter) because on-stream
text wears the app's typefaces.
Static by design: it takes plain strings, so the caller caches the frame and only re-rasterises when
a value changes — a strip that re-rendered every tick would be the compositor's most expensive layer
for no benefit.
Guard: ScoreboardRendererTests paints real frames and inspects pixels — plate colour AND opacity,
text colour reaching glyphs, plate-off leaves the area transparent, and an empty tile set returns no
frame at all.