From b56f6b0bc8f49aa77ce59c851dd0772b7f64f1fa Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Tue, 21 Jul 2026 08:59:08 +0200 Subject: [PATCH] fix(ui): T-1140 overlay bar wraps instead of overflowing; strip dev ids from tooltips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jeroen's first make atlas hands-on: 17 overlay chips in a single HBoxContainer row ran off the right screen edge (positioning also guessed the bar's width with a 520px fallback). The bar is now an HFlowContainer with ALIGNMENT_END; the viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned — verified via live capture, all 18 chips visible. OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game. Stripped to plain language; the visibility-ladder semantics (public signal / observable / needs corporate contact / needs insider access) stay. Co-Authored-By: Claude Fable 5 --- .pql/changelog/ticket_history/2026-07.sql | 8 ++++ .pql/changelog/tickets/2026-07.sql | 7 ++++ .../implant/apps/atlas/atlas_overlay_bar.gd | 11 ++++- client/ui/implant/apps/atlas/atlas_viewer.gd | 41 +++++++++++-------- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/.pql/changelog/ticket_history/2026-07.sql b/.pql/changelog/ticket_history/2026-07.sql index 3a7a1c372..51ecc40ee 100644 --- a/.pql/changelog/ticket_history/2026-07.sql +++ b/.pql/changelog/ticket_history/2026-07.sql @@ -1573,3 +1573,11 @@ Design change from the PR #185 Troblum review (binding, folded into the amendmen INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPRZZ0P42NHC1QR5S3737EV4', 'status', 'review', 'done', NULL, '2026-07-18 10:47:49', '2026-07-18 10:47:49.025', '2026-07-18 10:47:49.025', NULL, 'ba6fc0953709dac9cc737c7f5899fa6d', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPQXFFHXEEDQEM7PGXAH3534', 'status', 'review', 'done', NULL, '2026-07-18 10:47:49', '2026-07-18 10:47:49.028', '2026-07-18 10:47:49.028', NULL, '4ae28f9b2965c36f1c4b7e6f88c5355f', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPQXHDSMSVD3VADV38NG9STW', 'status', 'review', 'done', NULL, '2026-07-18 10:47:49', '2026-07-18 10:47:49.029', '2026-07-18 10:47:49.029', NULL, 'f329401a96278d3e8ee18598e2bc601a', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FQ9B977KGH3BWP1YQTN32TYM', 'description', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s). + +Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation.', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s). + +Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation. + +SUPERSEDED ENTRY MECHANIC (Jeroen, 2026-07-21 companion hands-on — recorded as the D-226 T-1124 SS5 entry revision): NO zoom-threshold LOD swap and NO DISTRICT_WINDOW_MIN_ZOOM — the earlier zoom-headroom note is moot. The planetary heightmap view becomes FIXED: remove drag-pan/wheel-zoom of the planetary canvas IN THIS TICKET, same change as the replacement (never strand close inspection), and update the header hint text accordingly. Entry: hovering the planetary heightmap shows a rectangle cursor representing the regional-mode bounds; click descends into the regional map centered on the click point''s derived DistrictPos (float-on-center rules carry over with pan-center read as click-point). In-mode behavior unchanged (pan-only refetch, 150ms debounce, D-227 cache, border-fade). Morph transition between modes explicitly deferred — the descent may cut. Open design point: the rectangle cursor is an affordance, not to scale (n=64 = ~131km = a few px on a planetary canvas) — resolve the honest representation (true-extent rectangle + zoom-in cut, or not-to-scale reticle with the real extent labeled) without implying more coverage than real. Note: set_view/get_view_offset (T-1120 capture API) must survive for the visual-golden harness even after user pan/zoom is removed.', NULL, '2026-07-21 06:58:26', '2026-07-21 06:58:26.870', '2026-07-21 06:58:26.870', NULL, '60f8b73d47b3a1ec7ddf8f6024f640b7', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FR6Y1CNA5JJ68RN897RM7FBC', 'status', 'backlog', 'in_progress', NULL, '2026-07-21 06:58:48', '2026-07-21 06:58:48.125', '2026-07-21 06:58:48.125', NULL, '07436c6c5318eca6cd011f2dd67c2dc1', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-07.sql b/.pql/changelog/tickets/2026-07.sql index 7d1d639c9..7d4aad061 100644 --- a/.pql/changelog/tickets/2026-07.sql +++ b/.pql/changelog/tickets/2026-07.sql @@ -2301,3 +2301,10 @@ Client anchors: SETTLEMENT_LABEL_MIN_ZOOM = 2.0 at atlas_marker_overlay.gd:113; IN-SCOPE boilerplate not named in the six points: the get/set_generation_quarter_footprints accessor pair on atlas_viewer.gd (folded into touch point 4, exactly as region_grid''s accessors ride its OVERLAY_DEFS entry; protocol dispatch calls the setter — see protocol.gd:436 pattern). Batch split (lead): points 1-2 (server) = Dudley, who also owns the gen_fixtures.rs edits for this batch (consider adding a populated quarter_footprints literal alongside T-1118''s region_grid literal — reviewers will expect the new layer to have fixture coverage); points 3-6 (client) + T-1118 = Stig. Dudley pins the quarter_footprints wire field names/types FIRST and messages Stig the shape before Stig wires the client half.', 'done', 'medium', NULL, 'server', 'D-226', '2026-07-16 17:25:23.533', '2026-07-18 10:47:49.028', NULL, '8b740a6c9acd80c1a96141ebf05d491b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FQ9B977KGH3BWP1YQTN32TYM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas regional map screen — client half (D-226 T-1124 amendment SS5)', 'Implement the D-226 T-1124 amendment SS5 (client half). protocol.gd passthrough: encode_atlas_layer_request gains the window params (serde-default absent = whole-body); atlas_response_from_raw gains district_window passthrough (the doc comment near protocol.gd:801 names this the one client-side spot). New regional viewing mode per SS5: zoom-threshold LOD swap in the existing AtlasViewer (new DISTRICT_WINDOW_MIN_ZOOM past SETTLEMENT_LABEL_MIN_ZOOM — not a new screen, not a nav push); morphology base layer lightness-modulated by elev_q (probe MORPHOLOGY_RGB hues); gen_dw_temp/gen_dw_moisture/gen_dw_veg toggle overlays (temp reuses the T-1118 region-ramp colorizer EXACTLY — one colorizer across both zoom levels; Marine=6 renders transparent; glaciation is an always-on ice-tint modifier, not a toggle); GENERATION_LEGEND entries with the 17-zone base folded to ~5 family rows. Client window cache keyed (body_id, center, n), LRU-evict only (D-227 determinism — no freshness check). Re-request on PAN ONLY when the view passes the held window''s edge (zoom never refetches); 150ms post-drag-release debounce; windows float on the pan center (not grid-snapped). Implant chrome: ImplantHeader anchor name + extent subtitle; map palettes stay out of theme accent roles (ACCENT_ACTIVE gold is the settlement marker''s). + +Implementation note (Tyre, T-1124 report): DISTRICT_WINDOW_MIN_ZOOM proposed at 6.0 but atlas_viewer.gd caps _view_zoom at MAX_ZOOM = 8.0 — only a 6.0-8.0 band for the whole in-window-zoom detail story the SS5 legibility argument depends on. Tunable client constant, not wire contract: either lower the swap threshold or raise MAX_ZOOM; decide with on-screen-scale math at implementation. + +SUPERSEDED ENTRY MECHANIC (Jeroen, 2026-07-21 companion hands-on — recorded as the D-226 T-1124 SS5 entry revision): NO zoom-threshold LOD swap and NO DISTRICT_WINDOW_MIN_ZOOM — the earlier zoom-headroom note is moot. The planetary heightmap view becomes FIXED: remove drag-pan/wheel-zoom of the planetary canvas IN THIS TICKET, same change as the replacement (never strand close inspection), and update the header hint text accordingly. Entry: hovering the planetary heightmap shows a rectangle cursor representing the regional-mode bounds; click descends into the regional map centered on the click point''s derived DistrictPos (float-on-center rules carry over with pan-center read as click-point). In-mode behavior unchanged (pan-only refetch, 150ms debounce, D-227 cache, border-fade). Morph transition between modes explicitly deferred — the descent may cut. Open design point: the rectangle cursor is an affordance, not to scale (n=64 = ~131km = a few px on a planetary canvas) — resolve the honest representation (true-extent rectangle + zoom-in cut, or not-to-scale reticle with the real extent labeled) without implying more coverage than real. Note: set_view/get_view_offset (T-1120 capture API) must survive for the visual-golden harness even after user pan/zoom is removed.', 'backlog', 'high', NULL, 'client', NULL, '2026-07-18 10:02:12.668', '2026-07-21 06:58:26.869', NULL, '946d4b4a331ee0dd7bf0ae1e08bb2da1', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FR6Y1CNA5JJ68RN897RM7FBC', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Companion Atlas friction — overlay bar overflows right edge; dev ticket ids in tooltips', 'Jeroen''s first make atlas hands-on (2026-07-21). Two UI defects on the shared Atlas viewer (visible in-game too): (1) the overlay toggle bar is a single HBoxContainer row — 17 chips at 42px min overflow the right screen edge (his 2000px window cut the bar mid-BAS); positioning also measured the bar''s own width with a 520px fallback, aggravating the overflow. Fix: HFlowContainer with ALIGNMENT_END, viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned. (2) OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game; stripped to plain language keeping the semantic content (visibility-ladder wording like observable/needs corporate contact stays). His third point — planetary map fixed + rectangle-cursor click-through entry — is a design revision recorded on D-226 (T-1124 SS5 entry revision) and folded into T-1138, not this bug.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-21 06:58:38.890', '2026-07-21 06:58:38.890', NULL, '685d1e4e2587c40abb524d7a9a64c4d7', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FR6Y1CNA5JJ68RN897RM7FBC', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Companion Atlas friction — overlay bar overflows right edge; dev ticket ids in tooltips', 'Jeroen''s first make atlas hands-on (2026-07-21). Two UI defects on the shared Atlas viewer (visible in-game too): (1) the overlay toggle bar is a single HBoxContainer row — 17 chips at 42px min overflow the right screen edge (his 2000px window cut the bar mid-BAS); positioning also measured the bar''s own width with a 520px fallback, aggravating the overflow. Fix: HFlowContainer with ALIGNMENT_END, viewer gives it everything right of the header (OVERLAY_BAR_HEADER_RESERVE) and rows wrap right-aligned. (2) OVERLAY_DEFS tooltips leaked dev provenance into player-facing text — ticket ids (#960, T-960, T-1046, T-1118, T-1119) and decision ids (D-181, D-226, D-243) mean nothing in-game; stripped to plain language keeping the semantic content (visibility-ladder wording like observable/needs corporate contact stays). His third point — planetary map fixed + rectangle-cursor click-through entry — is a design revision recorded on D-226 (T-1124 SS5 entry revision) and folded into T-1138, not this bug.', 'in_progress', 'medium', NULL, 'client', NULL, '2026-07-21 06:58:38.890', '2026-07-21 06:58:48.125', NULL, '1944f85140fa14cfe8a5676b6452bdf2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/client/ui/implant/apps/atlas/atlas_overlay_bar.gd b/client/ui/implant/apps/atlas/atlas_overlay_bar.gd index 64e3b3028..fe7418701 100644 --- a/client/ui/implant/apps/atlas/atlas_overlay_bar.gd +++ b/client/ui/implant/apps/atlas/atlas_overlay_bar.gd @@ -1,4 +1,4 @@ -extends HBoxContainer +extends HFlowContainer ## Overlay toggle bar for #835 AtlasViewer — nine MVP overlays plus two locked ## layers (#836, D-191 §7, D-181 signal visibility ladder). @@ -11,6 +11,11 @@ extends HBoxContainer ## Layout: [ALWAYS-ON] [TOGGLEABLE] [LOCKED]. Each row in viewer.get_overlay_defs() ## produces exactly one button, so adding or retiring an overlay is a one-file ## change — the bar and marker renderer stay in lockstep (review #7). +## +## HFlowContainer, not HBoxContainer: the overlay count outgrew one row +## (17 chips at 42px min overflowed the right screen edge — Jeroen, +## companion feedback 2026-07-21). The viewer gives the bar a constrained +## width (everything right of the header) and rows wrap right-aligned. const COLOR_PINNED: Color = Color("#c8d0e0") const COLOR_ACTIVE: Color = Color("#f0d060") @@ -23,7 +28,9 @@ var _buttons: Dictionary = {} # overlay_id -> Button func _init(viewer_ref = null) -> void: _viewer = viewer_ref - add_theme_constant_override("separation", 4) + alignment = FlowContainer.ALIGNMENT_END + add_theme_constant_override("h_separation", 4) + add_theme_constant_override("v_separation", 4) func _ready() -> void: diff --git a/client/ui/implant/apps/atlas/atlas_viewer.gd b/client/ui/implant/apps/atlas/atlas_viewer.gd index 20eff46b5..de09f16e4 100644 --- a/client/ui/implant/apps/atlas/atlas_viewer.gd +++ b/client/ui/implant/apps/atlas/atlas_viewer.gd @@ -33,6 +33,9 @@ const ZOOM_STEP: float = 1.15 const PANEL_WIDTH: float = 320.0 const PANEL_MARGIN: float = 16.0 +# Width reserved for the top-left header block (title + hint lines) that the +# wrapping overlay bar must not overlap. +const OVERLAY_BAR_HEADER_RESERVE: float = 360.0 # ── Colors ──────────────────────────────────────────────────────────────────── const COLOR_BG: Color = Color("#0d1117") @@ -96,80 +99,80 @@ const OVERLAY_DEFS: Array = [ "id": "population_density", "label": "POP", "group": "toggle", - "tooltip": "Population density (D-181 public)." + "tooltip": "Population density (public signal)." }, { "id": "production_zones", "label": "PRD", "group": "toggle", - "tooltip": "Production zones (D-181 observable — requires presence)." + "tooltip": "Production zones (observable — requires presence)." }, { "id": "corp_presence", "label": "CRP", "group": "toggle", - "tooltip": "Corporate presence — Tier 1 only (D-181 observable)." + "tooltip": "Corporate presence — Tier 1 only (observable)." }, { "id": "stockpile_weeks", "label": "STK", "group": "locked", - "tooltip": "Stockpile weeks — LOCKED. Needs corporate contact (D-181 semi-private)." + "tooltip": "Stockpile weeks — LOCKED. Needs corporate contact." }, { "id": "production_vs_baseline", "label": "BSL", "group": "locked", - "tooltip": "Production vs baseline — LOCKED. Needs insider access (D-181 private)." + "tooltip": "Production vs baseline — LOCKED. Needs insider access." }, { "id": "gen_l1_rivers", "label": "RVR", "group": "toggle", - "tooltip": "Layer 1 — river network (generation overlay, #960)." + "tooltip": "River network (generation overlay)." }, { "id": "gen_l1_basins", "label": "BAS", "group": "toggle", - "tooltip": "Layer 1 — drainage basins (generation overlay, #960)." + "tooltip": "Drainage basins (generation overlay)." }, { "id": "gen_l1_attractors", "label": "ATR", "group": "toggle", - "tooltip": "Layer 1 — geographic attractors (generation overlay, #960)." + "tooltip": "Geographic attractors (generation overlay)." }, { "id": "gen_district", "label": "MRPH", "group": "toggle", - "tooltip": "District morphology zones (generation overlay, T-1046/D-226)." + "tooltip": "District morphology zones (generation overlay)." }, { "id": "gen_l2_roads", "label": "RDS", "group": "toggle", "tooltip": - "Layer 2 — inter-settlement road/rail graph, colored by maintenance authority (generation overlay, T-960)." + "Inter-settlement road/rail graph, colored by maintenance authority (generation overlay)." }, { "id": "gen_l3_settlements", "label": "STL", "group": "toggle", - "tooltip": "Layer 3 — settlement placements, sized by population (generation overlay, T-960)." + "tooltip": "Settlement placements, sized by population (generation overlay)." }, { "id": "gen_region_grid", "label": "TMP", "group": "toggle", - "tooltip": "Region climate grid — mean temperature (generation overlay, T-1118/D-243)." + "tooltip": "Region climate — mean temperature (generation overlay)." }, { "id": "gen_l4_quarters", "label": "QTR", "group": "toggle", - "tooltip": "Layer 4 — quarter footprint, color=density shape=type (generation overlay, T-1119)." + "tooltip": "Quarter footprints — color is density, shape is district type (generation overlay)." }, ] @@ -949,9 +952,15 @@ func _position_overlay_bar() -> void: var sz: Vector2 = get_rect().size if sz == Vector2.ZERO: sz = Vector2(1280.0, 720.0) - # Top-right, leaving room for the header text - var bar_w: float = _overlay_bar.size.x if _overlay_bar.size.x > 0.0 else 520.0 - _overlay_bar.position = Vector2(sz.x - bar_w - PANEL_MARGIN, PANEL_MARGIN) + # Top-right, constrained to everything right of the header. The bar is an + # HFlowContainer with ALIGNMENT_END: give it the available width and it + # wraps into as many right-aligned rows as the chip count needs (17 chips + # overflowed the screen edge as a single HBox row — companion feedback, + # 2026-07-21). Height is Control-clamped to the flow's minimum, so it + # grows per row on its own. + var avail_w: float = maxf(sz.x - OVERLAY_BAR_HEADER_RESERVE - PANEL_MARGIN * 2.0, 200.0) + _overlay_bar.position = Vector2(sz.x - avail_w - PANEL_MARGIN, PANEL_MARGIN) + _overlay_bar.size = Vector2(avail_w, 0.0) # =============================================================================