diff --git a/docs/FUNCTIONAL.md b/docs/FUNCTIONAL.md
index bb0e71b..fb3b8a0 100644
--- a/docs/FUNCTIONAL.md
+++ b/docs/FUNCTIONAL.md
@@ -820,8 +820,12 @@ every change applies within one frame (no Pixi remount):
`VisibilityDistance(localPlayerDrive)` circles around LOCAL
planets; LOCAL planets are always exempt — the toggle is
named after the visible part of the map rather than the
- obscured one) plus the torus / no-wrap radio that switches
- the renderer mode while preserving the camera centre.
+ obscured one). The renderer always runs in torus mode; the
+ earlier torus / no-wrap radio was removed in F8 polish
+ (issue #48 п.8) because the topology is a server-side concept
+ rather than a per-session UI affordance. The renderer-side
+ no-wrap path is retained for the day the engine surfaces a
+ bounded-plane mode.
LOCAL planets are always rendered — they have no toggle. Every
other toggle defaults to ON. Hiding a planet cascades onto every
diff --git a/docs/FUNCTIONAL_ru.md b/docs/FUNCTIONAL_ru.md
index 615b423..0429bef 100644
--- a/docs/FUNCTIONAL_ru.md
+++ b/docs/FUNCTIONAL_ru.md
@@ -840,9 +840,12 @@ Directory-промоушен ([Раздел 5](#5-реестр-названий-
объединения окружностей
`VisibilityDistance(localPlayerDrive)` вокруг LOCAL-планет;
LOCAL-планеты всегда вне фильтра — тоггл назван по видимой
- области карты, а не по затемнённой) плюс радиогруппа
- «торус / без переноса», переключающая режим рендерера с
- сохранением центра камеры.
+ области карты, а не по затемнённой). Рендерер всегда работает
+ в торическом режиме; прежняя радиогруппа «торус / без
+ переноса» была удалена в полишинге F8 (issue #48 п.8),
+ поскольку топология карты — серверная сущность, а не
+ per-session UI-настройка. Код-путь без переноса в рендерере
+ оставлен на день, когда движок выставит режим bounded plane.
LOCAL-планеты отрисовываются всегда — для них тоггла нет.
Остальные тогглы по умолчанию включены. Скрытие планеты
diff --git a/ui/docs/design-system.md b/ui/docs/design-system.md
index 264f01a..fcafb7c 100644
--- a/ui/docs/design-system.md
+++ b/ui/docs/design-system.md
@@ -71,13 +71,24 @@ the colour block in `tokens.css`.
`theme.resolved` (`light` | `dark`), and `theme.setChoice(…)`. It
persists the choice, applies `data-theme`, and — while the choice is
`system` — follows OS theme changes via `matchMedia`.
-- The account menu (`account-menu.svelte`) exposes the picker. The
- default is `system` (it follows the OS preference); `light` / `dark`
- pin a theme.
+- The persisted picker lives in the lobby profile screen
+ ([`screens/profile-screen.svelte`](../frontend/src/lib/screens/profile-screen.svelte)) —
+ the in-game header is intentionally light on chrome and only carries
+ the volatile light/dark toggle described below. The default is
+ `system` (it follows the OS preference); `light` / `dark` pin a theme.
+- On top of the persisted choice the store carries an ephemeral
+ `theme.override` (`null` | `light` | `dark`). `setOverride(…)`
+ short-circuits `resolved` so the in-game toggle
+ ([`header/game-mode-theme-toggle.svelte`](../frontend/src/lib/header/game-mode-theme-toggle.svelte))
+ can flip the document theme without touching the lobby preference.
+ The override lives in memory only; the game shell calls
+ `theme.clearOverride()` on unmount, so leaving the game and re-entering
+ it re-projects the persisted choice from lobby.
The `app.html` guard and the store deliberately duplicate the
resolution logic (one runs before modules load, the other after) — keep
-them in sync.
+them in sync. The ephemeral override is intentionally absent from the
+pre-paint guard: it cannot survive a reload, only an in-tab session.
## Conventions
@@ -126,4 +137,5 @@ battle-scene palette, both defined in code rather than as tokens), the
overlay scrims, and the directional / deliberate drop shadows.
The default theme is **`system`** — it follows the OS light/dark
-preference; users can pin light or dark via the account-menu picker.
+preference; users pin light or dark via the lobby profile screen, and
+flip the in-game appearance volatilely through the header theme toggle.
diff --git a/ui/frontend/src/lib/active-view/map-toggles.svelte b/ui/frontend/src/lib/active-view/map-toggles.svelte
index 4ce09c1..9b74f0c 100644
--- a/ui/frontend/src/lib/active-view/map-toggles.svelte
+++ b/ui/frontend/src/lib/active-view/map-toggles.svelte
@@ -1,10 +1,15 @@
-
-
-
-
- {#if open}
-
-
-
-
-
-
-
- {/if}
-
-
-
diff --git a/ui/frontend/src/lib/header/game-mode-theme-toggle.svelte b/ui/frontend/src/lib/header/game-mode-theme-toggle.svelte
new file mode 100644
index 0000000..3f397d4
--- /dev/null
+++ b/ui/frontend/src/lib/header/game-mode-theme-toggle.svelte
@@ -0,0 +1,60 @@
+
+
+
+
+
+
diff --git a/ui/frontend/src/lib/header/header.svelte b/ui/frontend/src/lib/header/header.svelte
index b1dd0f5..eaf5191 100644
--- a/ui/frontend/src/lib/header/header.svelte
+++ b/ui/frontend/src/lib/header/header.svelte
@@ -2,9 +2,13 @@
Top header for the in-game shell. Composes the in-game ID strip
(race name @ game name) followed by the Phase 26 turn navigator (a
`← Turn N →` triplet with a popover of every turn), the view
-dropdown / hamburger, and the account menu. The sidebar-toggle slot
-to its left appears only on tablet viewports (768–1024 px) and is
-wired by `+layout.svelte`.
+dropdown / hamburger, and the in-game ephemeral light/dark theme
+toggle. The sidebar-toggle slot to its left appears only on tablet
+viewports (768–1024 px) and is wired by `+layout.svelte`.
+
+The persisted theme choice (and the language picker, logout, etc.)
+lives in the lobby — the in-game header carries only the ephemeral
+toggle for quick visual flips during a session.
The race name is read from the engine's `Report.race`, the game
name from the lobby's `GameSummary.gameName`. While either piece
@@ -24,7 +28,7 @@ absent until Phase 24 wires push-event state.
type GameStateStore,
} from "$lib/game-state.svelte";
import ViewMenu from "./view-menu.svelte";
- import AccountMenu from "./account-menu.svelte";
+ import GameModeThemeToggle from "./game-mode-theme-toggle.svelte";
import TurnNavigator from "./turn-navigator.svelte";
type Props = {
@@ -78,7 +82,7 @@ absent until Phase 24 wires push-event state.
⤧
-
+
diff --git a/ui/frontend/src/lib/i18n/locales/en.ts b/ui/frontend/src/lib/i18n/locales/en.ts
index 54cfd74..8107798 100644
--- a/ui/frontend/src/lib/i18n/locales/en.ts
+++ b/ui/frontend/src/lib/i18n/locales/en.ts
@@ -144,16 +144,9 @@ const en = {
"game.shell.menu.close_sidebar": "close sidebar",
"game.shell.menu.open_views": "open views menu",
"game.shell.menu.close_views": "close views menu",
- "game.shell.menu.account": "account",
- "game.shell.menu.settings": "settings",
- "game.shell.menu.sessions": "sessions",
- "game.shell.menu.theme": "theme",
- "game.shell.menu.theme_system": "system",
- "game.shell.menu.theme_light": "light",
- "game.shell.menu.theme_dark": "dark",
- "game.shell.menu.language": "language",
"game.shell.menu.return_to_lobby": "return to lobby",
- "game.shell.menu.logout": "logout",
+ "game.shell.theme_toggle.to_light": "switch to light theme",
+ "game.shell.theme_toggle.to_dark": "switch to dark theme",
"game.shell.coming_soon": "coming soon",
"game.shell.turn.label": "turn {turn}",
"game.shell.turn.list_item": "turn #{turn}",
@@ -183,9 +176,6 @@ const en = {
"game.map.toggles.unidentified_planets": "unidentified planets",
"game.map.toggles.unreachable_planets": "show unreachable planets",
"game.map.toggles.visible_hyperspace": "visible hyperspace",
- "game.map.toggles.wrap.label": "wrap scrolling",
- "game.map.toggles.wrap.torus": "torus",
- "game.map.toggles.wrap.no_wrap": "no-wrap",
"game.view.table": "table",
"game.view.table.planets": "planets",
"game.view.table.ship_classes": "ship classes",
@@ -293,7 +283,6 @@ const en = {
"game.inspector.planet.action.rename": "rename",
"game.inspector.planet.rename.title": "rename planet",
"game.inspector.planet.rename.confirm": "save",
- "game.inspector.planet.rename.cancel": "cancel",
"game.inspector.planet.rename.invalid.empty": "name cannot be empty",
"game.inspector.planet.rename.invalid.too_long": "name is too long (30 characters max)",
"game.inspector.planet.rename.invalid.starts_with_special": "name cannot start with a special character",
@@ -302,6 +291,8 @@ const en = {
"game.inspector.planet.rename.invalid.whitespace": "name cannot contain spaces",
"game.inspector.planet.rename.invalid.disallowed_character": "name contains disallowed characters",
"game.inspector.planet.production.title": "production",
+ "game.inspector.planet.production.main.aria": "production type",
+ "game.inspector.planet.production.main.placeholder": "(production)",
"game.inspector.planet.production.option.industry": "industry",
"game.inspector.planet.production.option.materials": "materials",
"game.inspector.planet.production.option.research": "research",
@@ -310,8 +301,14 @@ const en = {
"game.inspector.planet.production.research.weapons": "weapons",
"game.inspector.planet.production.research.shields": "shields",
"game.inspector.planet.production.research.cargo": "cargo",
+ "game.inspector.planet.production.target.research.aria": "research target",
+ "game.inspector.planet.production.target.research.placeholder": "(tech or science)",
+ "game.inspector.planet.production.target.ship.aria": "ship class",
+ "game.inspector.planet.production.target.ship.placeholder": "(ship class)",
"game.inspector.planet.production.ship.no_classes": "no ship classes designed yet",
- "game.inspector.planet.cargo.title": "cargo routes",
+ "game.inspector.planet.production.apply": "apply production change",
+ "game.inspector.planet.production.cancel": "discard production change",
+ "game.inspector.planet.cargo.placeholder": "cargo routes",
"game.inspector.planet.cargo.slot.col": "colonists",
"game.inspector.planet.cargo.slot.cap": "industry",
"game.inspector.planet.cargo.slot.mat": "materials",
@@ -602,6 +599,7 @@ const en = {
"game.inspector.ship_group.action.invalid.level": "level must be in ({current}, {max}]",
"game.inspector.ship_group.action.invalid.fleet_name": "fleet name does not match the entity-name rules",
+ "game.inspector.planet.ship_groups.race_filter.aria": "stationed race",
"game.inspector.planet.ship_groups.title": "stationed ship groups",
"game.inspector.planet.ship_groups.row.count": "{count} ships",
"game.inspector.planet.ship_groups.row.mass": "mass {mass}",
diff --git a/ui/frontend/src/lib/i18n/locales/ru.ts b/ui/frontend/src/lib/i18n/locales/ru.ts
index f9ddca3..b2fb2de 100644
--- a/ui/frontend/src/lib/i18n/locales/ru.ts
+++ b/ui/frontend/src/lib/i18n/locales/ru.ts
@@ -145,16 +145,9 @@ const ru: Record = {
"game.shell.menu.close_sidebar": "закрыть боковую панель",
"game.shell.menu.open_views": "открыть меню видов",
"game.shell.menu.close_views": "закрыть меню видов",
- "game.shell.menu.account": "аккаунт",
- "game.shell.menu.settings": "настройки",
- "game.shell.menu.sessions": "сессии",
- "game.shell.menu.theme": "тема",
- "game.shell.menu.theme_system": "системная",
- "game.shell.menu.theme_light": "светлая",
- "game.shell.menu.theme_dark": "тёмная",
- "game.shell.menu.language": "язык",
"game.shell.menu.return_to_lobby": "вернуться в лобби",
- "game.shell.menu.logout": "выйти",
+ "game.shell.theme_toggle.to_light": "переключить на светлую тему",
+ "game.shell.theme_toggle.to_dark": "переключить на тёмную тему",
"game.shell.coming_soon": "скоро будет",
"game.shell.turn.label": "ход {turn}",
"game.shell.turn.list_item": "ход #{turn}",
@@ -184,9 +177,6 @@ const ru: Record = {
"game.map.toggles.unidentified_planets": "неопознанные планеты",
"game.map.toggles.unreachable_planets": "показывать недостижимые планеты",
"game.map.toggles.visible_hyperspace": "видимое гиперпространство",
- "game.map.toggles.wrap.label": "перенос карты",
- "game.map.toggles.wrap.torus": "тор",
- "game.map.toggles.wrap.no_wrap": "без переноса",
"game.view.table": "таблица",
"game.view.table.planets": "планеты",
"game.view.table.ship_classes": "классы кораблей",
@@ -294,7 +284,6 @@ const ru: Record = {
"game.inspector.planet.action.rename": "переименовать",
"game.inspector.planet.rename.title": "переименование планеты",
"game.inspector.planet.rename.confirm": "сохранить",
- "game.inspector.planet.rename.cancel": "отмена",
"game.inspector.planet.rename.invalid.empty": "имя не может быть пустым",
"game.inspector.planet.rename.invalid.too_long": "имя слишком длинное (максимум 30 символов)",
"game.inspector.planet.rename.invalid.starts_with_special": "имя не может начинаться со спецсимвола",
@@ -303,6 +292,8 @@ const ru: Record = {
"game.inspector.planet.rename.invalid.whitespace": "имя не может содержать пробелы",
"game.inspector.planet.rename.invalid.disallowed_character": "имя содержит недопустимые символы",
"game.inspector.planet.production.title": "производство",
+ "game.inspector.planet.production.main.aria": "тип производства",
+ "game.inspector.planet.production.main.placeholder": "(производство)",
"game.inspector.planet.production.option.industry": "промышленность",
"game.inspector.planet.production.option.materials": "сырьё",
"game.inspector.planet.production.option.research": "исследование",
@@ -311,8 +302,14 @@ const ru: Record = {
"game.inspector.planet.production.research.weapons": "оружие",
"game.inspector.planet.production.research.shields": "щиты",
"game.inspector.planet.production.research.cargo": "трюм",
+ "game.inspector.planet.production.target.research.aria": "цель исследования",
+ "game.inspector.planet.production.target.research.placeholder": "(технология или наука)",
+ "game.inspector.planet.production.target.ship.aria": "класс корабля",
+ "game.inspector.planet.production.target.ship.placeholder": "(класс корабля)",
"game.inspector.planet.production.ship.no_classes": "классы кораблей ещё не спроектированы",
- "game.inspector.planet.cargo.title": "грузовые маршруты",
+ "game.inspector.planet.production.apply": "применить изменение производства",
+ "game.inspector.planet.production.cancel": "отменить изменение производства",
+ "game.inspector.planet.cargo.placeholder": "грузовые маршруты",
"game.inspector.planet.cargo.slot.col": "колонисты",
"game.inspector.planet.cargo.slot.cap": "промышленность",
"game.inspector.planet.cargo.slot.mat": "сырьё",
@@ -603,6 +600,7 @@ const ru: Record = {
"game.inspector.ship_group.action.invalid.level": "уровень должен быть в ({current}, {max}]",
"game.inspector.ship_group.action.invalid.fleet_name": "имя флота не соответствует правилам имён сущностей",
+ "game.inspector.planet.ship_groups.race_filter.aria": "раса в орбите",
"game.inspector.planet.ship_groups.title": "корабли на орбите",
"game.inspector.planet.ship_groups.row.count": "{count} кораблей",
"game.inspector.planet.ship_groups.row.mass": "масса {mass}",
diff --git a/ui/frontend/src/lib/inspectors/planet.svelte b/ui/frontend/src/lib/inspectors/planet.svelte
index 1c2073f..79415de 100644
--- a/ui/frontend/src/lib/inspectors/planet.svelte
+++ b/ui/frontend/src/lib/inspectors/planet.svelte
@@ -1,16 +1,20 @@
-