/* JetBrains Mono под OFL 1.1, текст лицензии — /assets/fonts/OFL-JetBrainsMono.txt.
   Файлы взяты готовыми вариативными разбиениями Google Fonts и не изменялись;
   `unicode-range` скопированы оттуда же.

   Источником стоит только `url()`. `local()` не используется сознательно: с ним
   браузер сначала берёт шрифт с таким именем из системы читателя, а что там
   установлено — другая версия, чужой шрифт, ничего — мы не знаем, и от этого
   зависят метрики всего интерфейса. Ради этого задача и затевалась. */
/* latin */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* cyrillic */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* cyrillic-ext */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-var-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: "Cartula";
  src: url("/assets/fonts/cartula.woff2") format("woff2");
  font-display: swap;
}
:root {
  color-scheme: dark;
  /* Системный стек остаётся последним рубежом на случай, если woff2 не доедет:
     тогда интерфейс снова разъедется по системам, но останется читаемым. */
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Программные лигатуры JetBrains Mono сделаны через `calt`, а его браузер
     включает по умолчанию. Имя игрока — любой печатный Unicode, поэтому ник
     вида `->` или `x != y` молча превратился бы в операторный знак в ростере,
     журнале и дипломатии. `none` гасит и `contextual`, то есть именно `calt`. */
  font-variant-ligatures: none;
  background: #151a24;
  color: #e7e1d3;
}
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  min-width: 320px;
  overflow: hidden;
}
.game-app {
  height: 100vh;
  overflow: hidden;
}
.lobby-screen {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 16px;
  min-height: 100vh;
  padding: 24px;
}
.lobby-screen[hidden],
.game-screen[hidden] {
  display: none;
}
/* The same wordmark the landing and lobby headers carry; the title is the
   image's alt text, so the heading keeps its name for assistive tech. */
.lobby-wordmark {
  margin: 0;
  width: 300px;
}
.lobby-wordmark img {
  display: block;
  width: 100%;
  height: auto;
}
.lobby-card {
  width: min(420px, 100%);
  background: #1d2433;
  border: 1px solid #3a4558;
  border-radius: 6px;
  padding: 16px;
}
.game-language-picker {
  justify-content: space-between;
  width: min(420px, 100%);
  padding: 12px 16px;
  background: #1d2433;
  border: 1px solid #3a4558;
  border-radius: 6px;
}
.lobby-card h2 {
  margin: 0 0 10px;
  color: #e0a23a;
  font-size: 14px;
}
.running-game-card p {
  margin: 0 0 12px;
  color: #9ea6b3;
  font-size: 13px;
  line-height: 1.45;
}
.field-row {
  display: grid;
  grid-template-columns: 90px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  color: #9ea6b3;
  font-size: 13px;
}
.text-input,
.map-select {
  min-width: 0;
  background: #151a24;
  color: #e7e1d3;
  border: 1px solid #3a4558;
  border-radius: 4px;
  padding: 8px 10px;
  font: inherit;
}
.map-select {
  width: 100%;
}
.lobby-value {
  min-width: 0;
  padding: 8px 10px;
  color: #e7e1d3;
  background: #171d29;
  border-left: 2px solid #3a4558;
}
.lobby-value[hidden],
.map-select[hidden],
.text-input[hidden],
.lobby-actions[hidden] {
  display: none;
}
.lobby-setting-row {
  margin-top: 10px;
}
.game-button {
  background: #c78232;
  color: #fff;
  border: 0;
  border-radius: 4px;
  padding: 8px 14px;
  font: inherit;
  cursor: pointer;
}
.game-button:hover {
  background: #e09a3e;
}
.game-button:disabled {
  opacity: 0.45;
  cursor: default;
}
.game-button--quiet {
  background: #1d2433;
  color: #9ea6b3;
  border: 1px solid #3a4558;
}
/* `:not(:disabled)` because red means leaving or cancelling — an offer of an
   action. A button that cannot be pressed should not make it. */
.game-button--quiet:hover:not(:disabled) {
  background: #a83a32;
  color: #fff;
}
#join {
  width: 100%;
  margin-top: 12px;
}
.color-palette {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-height: 30px;
  margin-top: 12px;
}
.color-palette[hidden] {
  display: none;
}
.color-swatch {
  width: 26px;
  height: 26px;
  background: var(--player-color);
  border: 2px solid #151a24;
  border-radius: 50%;
  box-shadow: 0 0 0 1px #3a4558;
  cursor: pointer;
}
.color-swatch:hover,
.color-swatch.is-selected {
  box-shadow: 0 0 0 2px #e7e1d3;
}
.player-color {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 7px;
  background: var(--player-color);
  border-radius: 50%;
  box-shadow: 0 0 0 1px #151a24, 0 0 0 2px #3a4558;
  vertical-align: -1px;
}
.lobby-error {
  margin-top: 8px;
  color: #c45248;
  font-size: 12px;
}
.lobby-error:empty {
  display: none;
}
.player-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.player-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin: 4px 0;
  padding: 8px 10px;
  background: #151a24;
  border-left: 3px solid #a83a32;
}
.player-row-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 22px;
}
.player-remove {
  /* `padding: 0` is load-bearing: a button carries the browser's own padding,
     about 1px 6px, and border-box then leaves 8px of content for a 13px icon —
     it does not fit and stops being centred. The text × it replaced was 7px
     wide, so the defect only appears once the mark becomes a drawing. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 22px;
  height: 22px;
  background: transparent;
  color: #9ea6b3;
  border: 1px solid #3a4558;
  border-radius: 50%;
  font: inherit;
  line-height: 1;
  cursor: pointer;
}
.player-remove svg,
.modal-list-remove svg {
  width: 13px;
  height: 13px;
}
.player-remove:hover {
  color: #fff;
  border-color: #c45248;
  background: #a83a32;
}
.player-list li.is-ready {
  border-left-color: #c78232;
}
.player-list li.is-disconnected {
  opacity: 0.55;
}
.lobby-actions {
  display: flex;
  gap: 10px;
  margin-top: 12px;
}
.lobby-actions .game-button {
  flex: 1;
}
/* The brain picker sits next to the button that uses it, and gives it room:
   the names are longer than the word Add. */
#add-bot-row .map-select {
  flex: 2;
}
.game-screen {
  --topbar-h: 38px;
  --pennant-w: 60px;
  --pennant-w-nav: 48px;
  --pennant-left: 14px;
  --pennant-gap: 10px;
  --pennant-hover-shift: 4px;
  --pennant-extend: 14px;
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
  /* Nothing on the game screen is prose: a drag over the panel used to paint
     half of it blue. The fields that take typing are in the lobby, not here. */
  user-select: none;
}
/* One cursor for the whole game screen — the arrow. The hand, the crosshair
   and the grab used to say "this reacts", and every element here reacts; what
   was left was a cursor that flickered from shape to shape across the map.
   Scoped to the screen: the same buttons keep their hand in the lobby and on
   the public pages, which are not part of this decision. */
.game-screen,
.game-screen * {
  cursor: default;
}
.game-topbar {
  position: relative;
  z-index: 12;
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--topbar-h);
  padding: 6px 14px;
  background: #1c2433;
  border-bottom: 2px solid #3a4558;
}
#back-to-lobby {
  margin-left: auto;
  padding: 4px 10px;
  font-size: 12px;
  white-space: nowrap;
}
.turn-label {
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  white-space: nowrap;
}
.turn-label-word {
  color: #9ea6b3;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.055em;
}
.turn-number {
  color: #e0a23a;
  font-size: 23px;
  font-weight: 700;
  line-height: 1;
}
.resource-bar {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 16px;
  min-width: 0;
  flex: 1;
  margin-left: calc(var(--pennant-left) + var(--pennant-w) + var(--pennant-gap));
  font-size: 16px;
}
.resource {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: #e7e1d3;
  white-space: nowrap;
}
.resource svg {
  width: 16px;
  height: 16px;
  color: #c78232;
  flex: none;
}
.resource-delta {
  color: #9ea6b3;
  font-size: 13px;
}
.top-nav {
  position: absolute;
  top: calc(var(--topbar-h) - var(--pennant-extend));
  left: calc(var(--pennant-left) + var(--pennant-w) + var(--pennant-gap));
  display: flex;
  align-items: flex-start;
  gap: var(--pennant-gap);
  z-index: 11;
}
.pennant {
  position: relative;
  border: 0;
  background: #c78232;
  width: var(--pennant-w);
  height: 64px;
  padding: 0;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% calc(100% - 15px), 0 100%);
  transition: transform 0.1s ease;
}
.pennant:hover,
.pennant.is-active {
  transform: translateY(var(--pennant-hover-shift));
}
.pennant-fill {
  position: absolute;
  inset: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #9ea6b3;
  font: inherit;
  font-size: 10px;
  line-height: 1.2;
  text-align: center;
  padding: 6px 4px 14px;
  background: #232c3e;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 1px), 50% calc(100% - 15px), 0 calc(100% - 1px));
}
.top-nav .pennant {
  width: var(--pennant-w-nav);
  height: calc(64px + var(--pennant-extend));
  background: #606060;
}
.top-nav .pennant .pennant-fill {
  background: linear-gradient(120deg, #3a4558, color-mix(in srgb, #3a4558 60%, black) 85%);
}
.top-nav .pennant:hover .pennant-fill,
.top-nav .pennant.is-active .pennant-fill {
  filter: brightness(1.15);
}
.top-nav .pennant svg {
  width: 28px;
  height: 28px;
  margin-top: 8px;
  color: #f1ae52;
}
.top-nav .pennant:hover svg,
.top-nav .pennant.is-active svg {
  color: #f8c67c;
}
.pennant--player {
  position: absolute;
  top: calc(-1 * var(--pennant-extend));
  left: var(--pennant-left);
  z-index: 2;
  height: calc(var(--topbar-h) + 64px + var(--pennant-extend));
}
.pennant--player .pennant-fill {
  background: linear-gradient(120deg, var(--player-color, #232c3e), color-mix(in srgb, var(--player-color, #232c3e) 40%, black) 85%);
}

.pennant--player .heraldic-emblem { width: 42px; height: 42px; }
.pennant--player:hover .pennant-fill,
.pennant--player.is-active .pennant-fill {
  filter: brightness(1.15);
}
.game-main {
  position: relative;
  min-height: 0;
  flex: 1;
}
.map-stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #151a24;
}
.map-stage svg {
  width: 100%;
  height: 100%;
  display: block;
}
/* The map used to cast its shadow with a CSS filter on this element. A filter
   here means the browser renders the whole map — every region, every icon, every
   label — into an offscreen buffer and blurs it, and it has to do that again for
   every frame of a pan. It cost 7-21 ms a frame for a decoration. The shadow is
   now a static image drawn once behind the map; see renderMapShadow. */
.game-map .land-regions {
  filter: drop-shadow(0 0 3px rgb(0 0 0 / 0.6));
}
.map-region {
}

.game-topo-biomes {
  opacity: 0.45;
}
.game-army-icon {
  filter: drop-shadow(0 3px 3px rgb(0 0 0 / 0.45));
  overflow: visible;
}
.game-army-icon.is-selected {
  filter: drop-shadow(0 0 5px #ffd84d) drop-shadow(0 3px 3px rgb(0 0 0 / 0.45));
}
/* The march being ordered this turn: the whole route, step by step, not a
   straight line to the destination. */
.game-move-arrow {
  fill: none;
  stroke: #ffd84d;
  stroke-width: 2;
  stroke-linecap: butt;
  stroke-linejoin: round;
  pointer-events: none;
}
/* Speed control, shown in place of End turn to someone watching rather than
   playing: slower, pause/play, faster, and the current multiplier. */
.pace-controls {
  display: flex;
  align-items: center;
  gap: 4px;
}
.pace-controls[hidden] {
  display: none;
}
/* A quiet button in a smaller size, nothing more: it used to carry its own
   border, fill and radius, and the fill had drifted a digit from the quiet
   one's — #1d2432 against #1d2433. Only the measurements are its own now.
   The hover is: `--quiet` turns red, which belongs to leaving and cancelling,
   and choosing a speed is neither. */
.pace-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  padding: 3px 6px;
  font-size: 13px;
  line-height: 1;
}
.pace-button:hover:not(:disabled) {
  border-color: #6b7996;
  background: #263042;
  color: #e7e1d3;
}
/* One em of the button's own 13px, so the row scales with its type. */
.pace-button svg {
  width: 1em;
  height: 1em;
}
.pace-speed {
  min-width: 26px;
  color: #cdc6b4;
  font-size: 12px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* A march already under way, as opposed to an order queued this turn: same
   colour, but dashed and dimmer so a fresh arrow reads on top of it. */
.game-march-route {
  fill: none;
  stroke: #ffd84d;
  stroke-width: 1.5;
  stroke-dasharray: 5 5;
  stroke-linejoin: round;
  opacity: 0.5;
  pointer-events: none;
}
.game-move-arrow-head {
  fill: none;
  stroke: #ffd84d;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}

/* The fog of war: land minus what the player can see (3.8.1). Neutral and dark
   rather than tinted — it dims, it does not recolour, so a realm's colour under
   it is still that realm's colour. Alpha is the one number here worth arguing
   about: it has to make the boundary of sight obvious at a glance while leaving
   owners and names legible underneath. */
.game-fog {
  fill: #0e1116;
  opacity: 0.42;
  pointer-events: none;
}
/* A settlement out of sight. Settlements draw above the veil so that no
   outline ever crosses a name plate, so the veil cannot dim them — each dims
   itself instead, to about the same shade: the veil keeps 0.58 of a colour and
   adds a little of its own near-black, brightness only keeps the 0.58. Close
   enough to read as one darkness; chosen by eye, not by equation. A filter
   per image is affordable for dozens of settlements; the biomes could never
   have it (see .game-topo-biomes). */
.game-topo-settlements .is-unseen {
  filter: brightness(0.58);
}
.side-column {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  min-height: 0;
  padding: 12px;
  pointer-events: none;
}
.selection-panel {
  flex: 0 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 14px;
  font-size: 12px;
  background: #1d2433;
  border: 2px solid #3a4558;
  border-radius: 6px;
  pointer-events: auto;
}
.selection-panel h2 {
  margin: 0 0 10px;
  color: #e0a23a;
  font-size: 14px;
}
/* The technical id of what the panel shows, in its top-right corner. Floated
   inside the heading rather than positioned against the panel, so it scrolls
   with the content it labels. */
.panel-id {
  float: right;
  margin-left: 10px;
  color: #6b7381;
  font-size: 11px;
  font-weight: 400;
  line-height: 20px;
}
/* What the thing in the heading is — a settlement's stage and owner — as one
   quiet line under the name rather than two rows of statistics. */
.panel-subtitle {
  margin: -6px 0 10px;
  color: #9ea6b3;
  font-size: 12px;
}
.region-illustration {
  position: relative;
  margin-bottom: 12px;
}
.region-image {
  display: block;
  width: 100%;
  height: 140px;
  object-fit: cover;
  border: 1px solid #3a4558;
  border-radius: 4px;
}
.region-owner-pennant-shadow {
  position: absolute;
  top: 1px;
  left: 12px;
  filter: drop-shadow(1px 0 2px rgb(0 0 0 / 0.38)) drop-shadow(2px 0 4px rgb(0 0 0 / 0.28));
}
.region-owner-pennant {
  display: grid;
  width: 42px;
  height: 48px;
  padding-bottom: 7px;
}
.selection-panel h3 {
  margin: 14px 0 6px;
  color: #c78232;
  font-size: 12px;
}
.selection-panel h2 + h3 {
  margin-top: 0;
}
.lens-panel {
  flex-shrink: 0;
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 3px 6px;
  background: #1d2433;
  border: 2px solid #3a4558;
  border-radius: 6px;
  pointer-events: auto;
}
.lens-title {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0 8px 0 4px;
  color: #9ea6b3;
  font-size: 11px;
  letter-spacing: 0.02em;
  line-height: 1;
}
.lens-button {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5px 4px;
  background: transparent;
  color: #fff;
  border: 1px solid transparent;
  border-radius: 4px;
  font: inherit;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
/* Only the fill moves: at rest the icon is already white, so a hover colour
   could only darken it — which reads as the button going away, not waking. */
.lens-button:hover {
  background: #232c3d;
}
.lens-button.is-active {
  background: #2a3446;
  color: #e0a23a;
  border-color: #c78232;
}
.lens-icon {
  display: flex;
}
/* `block` so the span wraps the drawing exactly: left inline, it would carry
   the font's descender gap under it and sit high in the strip. */
.lens-title svg,
.lens-icon svg {
  display: block;
  width: 14px;
  height: 14px;
}
/* Country names drawn over claimed territory (see js/game/state-labels.js).
   The light halo keeps the name readable over any player color. */
/* Both set by js/mapgen/render/label-visibility.js: the first when a label would
   render too small to read, the second on an icon whose macroregion name is
   currently drawn over it. display:none keeps them out of rendering entirely. */
.label-zoom-hidden,
.covered-icon-hidden {
  display: none;
}
.state-label {
  font-family: "Cartula", Georgia, serif;
  text-anchor: middle;
  dominant-baseline: middle;
  overflow: visible;
  paint-order: stroke fill;
  stroke: rgb(255 255 255 / 0.5);
  stroke-width: 3;
  stroke-linejoin: round;
  pointer-events: none;
}
.turn-bar {
  flex-shrink: 0;
  min-height: 48px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  padding: 6px;
  background: #1d2433;
  border: 2px solid #3a4558;
  border-radius: 6px;
  pointer-events: auto;
}
#end-turn {
  width: 136px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 12px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.035em;
  text-shadow: 0 1px 1px rgb(0 0 0 / 0.35);
  text-transform: uppercase;
  white-space: nowrap;
}
#end-turn[hidden] {
  display: none;
}
.turn-timer {
  flex-shrink: 0;
  width: 19px;
  height: 19px;
  /* Drain from the top, the way a clock is read. */
  transform: rotate(-90deg);
}
.turn-timer-track,
.turn-timer-arc {
  fill: none;
  stroke-width: 3.3;
  stroke-linecap: round;
}
.turn-timer-track {
  stroke: #8a551e;
}
.turn-timer-arc {
  stroke: #fff;
  /* One dash the length of the whole circle (pathLength="1"), so the offset is
     the share already spent: 0 draws the full ring, 1 draws nothing. */
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.panel-actions {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
}
.range-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 5px;
  color: #9ea6b3;
}
/* Единственный `type="range"` в клиенте. Без правил ниже его рисует система
   целиком: бегунок берёт СИСТЕМНЫЙ акцентный цвет, то есть на macOS зависит от
   того, какой цвет человек выбрал в настройках, а собственная высота у него
   разная в трёх движках (около 16, 24 и 28 px) — вместе с ней ехала и высота
   строки. Здесь всё задано нами. */
.range-row input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  /* Ромб 16 px по диагонали занимает около 22.6 px — при меньшей высоте строки
     его подрезает. */
  width: 100%;
  height: 28px;
  background: transparent;
}
.range-row input[type="range"]:focus {
  outline: none;
}
/* `content-box` задан явно: правило `* { box-sizing: border-box }` на
   псевдоэлементы не распространяется, и без этого высота содержимого дорожки
   зависит от UA — 6 px или 4 px. Отступ язычка ниже считается именно от шести. */
.range-row input[type="range"]::-webkit-slider-runnable-track {
  box-sizing: content-box;
  height: 6px;
  background:
    linear-gradient(#c78232, #c78232) 0 / var(--fill, 0%) 100% no-repeat,
    #151a24;
  border: 1px solid #3a4558;
  border-radius: 0;
}
/* Обводка ТОГО ЖЕ цвета, что заливка, — намеренно. Собственного канта у ромба
   нет: на золотой половине обводка растворяется в заливке и ромб держится одним
   зазором цвета фона, на тёмной читается как продолжение полосы. Первая
   редакция несла более тёмный кант `#b87327`, и ромб на золотой половине в
   заливку проваливался. Правка «на кант потемнее» будет регрессом. */
.range-row input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  box-sizing: border-box;
  /* 16 против чётной дорожки, чтобы отступ был целым: при 15 выходило -4.5px,
     браузер округлял его как хотел и ромб уезжал на пиксель вниз. */
  width: 16px;
  height: 16px;
  margin-top: -5px; /* (6 - 16) / 2 */
  background: #e0a23a;
  border: 3px solid #c78232;
  border-radius: 2px;
  box-shadow: 0 0 0 2px #151a24;
  transform: rotate(45deg);
}
.range-row input[type="range"]:active::-webkit-slider-thumb {
  background: #e09a3e;
}
.range-row input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 2px #151a24, 0 0 0 4px #e0a23a;
}
.range-row input[type="range"]:disabled {
  opacity: 0.45;
}
/* В Firefox заполнение рисуется своим псевдоэлементом, поэтому градиент по
   переменной ему не нужен. */
.range-row input[type="range"]::-moz-range-track {
  box-sizing: content-box;
  height: 6px;
  background: #151a24;
  border: 1px solid #3a4558;
}
.range-row input[type="range"]::-moz-range-progress {
  height: 6px;
  background: #c78232;
}
.range-row input[type="range"]::-moz-range-thumb {
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  background: #e0a23a;
  border: 3px solid #c78232;
  border-radius: 2px;
  box-shadow: 0 0 0 2px #151a24;
  transform: rotate(45deg);
}
.range-row input[type="range"]:active::-moz-range-thumb {
  background: #e09a3e;
}
.range-row output {
  color: #e7e1d3;
  text-align: right;
}
.panel-action {
  width: 100%;
}
/* A roster row whose army is not in the selection: the quiet grade of button,
   so the filled rows are the chosen ones. Not `.game-button--quiet`, whose red
   hover means leaving or cancelling — picking an army is neither. */
.panel-action--quiet {
  background: #1d2433;
  color: #9ea6b3;
  border: 1px solid #3a4558;
}
.panel-action--quiet:hover {
  background: #232c3d;
  color: #e7e1d3;
}
/* The second line of a roster row: what this one host is up to. */
.panel-action-note {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  opacity: 0.8;
}
/* Sits in a line of text, so it is sized and centred by that line, not by px. */
.panel-action svg {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
}
.pending-note,
.action-note {
  color: #9ea6b3;
  font-size: 11px;
  text-align: right;
}
/* The queued order and its cross on one line, the cross at the edge where the
   modal keeps it. */
.pending-note {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}
.empty {
  color: #9ea6b3;
}
/* Events of the turn just resolved. A list rather than the stat grid: these are
   happenings, not properties, and two battles over one region have to stay two
   readable items instead of running together. */
.event-list {
  display: grid;
  gap: 0;
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
}
.event-item {
  border-bottom: 1px solid #252e3f;
  color: #e7e1d3;
  padding: 6px 0 6px 10px;
  border-left: 2px solid #4a5568;
}
.event-item--battle {
  border-left-color: #a3352c;
}
.event-item--dispersed {
  border-left-color: #8d8478;
}
.stat-grid {
  display: grid;
  gap: 0;
}
.stat-row {
  display: grid;
  grid-template-columns: 1fr auto;
}
.stat-label,
.stat-value {
  border-bottom: 1px solid #252e3f;
  padding: 5px 0;
}
.stat-label {
  color: #9ea6b3;
  padding-right: 10px;
}
.stat-value {
  color: #e7e1d3;
  text-align: right;
}
/* The number's movement over the last turn, beside it as in the top bar. A
   fixed column, so the counts above one another stay in one column too rather
   than each being pushed left by the width of its own delta. */
.stat-delta {
  display: inline-block;
  min-width: 5ch;
  margin-left: 6px;
  color: #9ea6b3;
  font-size: 11px;
  text-align: right;
}
.modal-root {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: rgb(0 0 0 / 0.55);
}
.modal-root[hidden] {
  display: none;
}
.modal-dialog {
  width: min(520px, calc(100% - 48px));
  max-height: 80%;
  overflow: auto;
  background: #1d2433;
  border: 2px solid #3a4558;
  border-radius: 6px;
  padding: 14px;
  font-size: 12px;
}
.modal-head {
  margin-bottom: 10px;
}
.modal-head h2 {
  margin: 0;
  color: #e0a23a;
  font-size: 14px;
}
.modal-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.modal-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin: 4px 0;
  padding: 8px 10px;
  background: #151a24;
  color: #e7e1d3;
}
/* The orders modal's second half: what could be ordered and was not. A row is
   a link to the thing — the same dark strip as a queue row, but the whole of
   it is the button, and there is nothing to remove. */
.modal-subtitle {
  margin: 14px 0 6px;
  color: #8fa8bf;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}
.modal-subtitle--head {
  margin-top: 18px;
  padding-top: 12px;
  border-top: 1px solid #2a3346;
  color: #e0a23a;
  font-size: 11px;
}
.modal-list--links li {
  padding: 0;
}
.modal-link {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  color: inherit;
  border: 0;
  font: inherit;
  text-align: left;
}
.modal-link:hover,
.modal-link:focus-visible {
  background: transparent;
  outline: none;
  box-shadow: inset 0 0 0 1px #e0a23a;
}
.modal-link-aside {
  color: #9ea6b3;
}
.modal-list-remove {
  /* Same circle as `.player-remove`, `padding: 0` included — see there. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 22px;
  height: 22px;
  background: transparent;
  color: #9ea6b3;
  border: 1px solid #3a4558;
  border-radius: 50%;
  font: inherit;
  line-height: 1;
}
.modal-list-remove:hover {
  color: #fff;
  border-color: #c45248;
  background: #a83a32;
}
/* The event journal: what the turn just resolved did, over the map in the
   bottom-left corner. Three signals, three questions — brightness says whether
   it concerns you, the bar whether it cost you something, the coloured word what
   it is about. */
.journal {
  position: absolute;
  left: 12px;
  bottom: 12px;
  z-index: 5;
  width: 380px;
  max-width: calc(100% - 24px);
  background: rgb(21 26 36 / 0.92);
  border: 1px solid #3a4558;
  border-radius: 6px;
  font-size: 11px;
  color: #e7e1d3;
  display: grid;
  gap: 6px;
  padding: 8px;
}
.journal[hidden] {
  display: none;
}
.journal-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.journal-title {
  color: #e0a23a;
  letter-spacing: 1px;
}
.journal-count {
  color: #e0a23a;
}
.journal-filters {
  display: flex;
  gap: 4px;
  margin-left: auto;
}
.journal-filter {
  background: transparent;
  color: #9ea6b3;
  border: 1px solid #3a4558;
  border-radius: 3px;
  padding: 2px 7px;
  font: inherit;
}
.journal-filter:hover {
  color: #e7e1d3;
}
.journal-filter.is-active {
  background: #2a3346;
  color: #e7e1d3;
  border-color: #4a5568;
}
.journal-collapse {
  background: transparent;
  border: 1px solid #3a4558;
  border-radius: 3px;
  color: #9ea6b3;
  font: inherit;
  padding: 2px 6px;
}
.journal-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 2px;
  max-height: 168px;
  overflow-y: auto;
}
/* Every line carries a bar, grey unless it cost something: the column on the
   left has to read as a column, not as a ragged edge. */
.journal-line {
  display: grid;
  grid-template-columns: 12px 1fr;
  gap: 8px;
  /* Not `baseline`: a drawing has none, so it would sit by the bottom of its
     box, and the marks whose weight is off-centre would hang below the line. */
  align-items: center;
  padding: 4px 6px;
  border-left: 2px solid #4a5568;
}
.journal-line:hover {
  background: #1d2433;
}
.journal-line--loss {
  border-left-color: #a3352c;
}
/* Somebody else's business, dimmed whole — names and numbers with it — so the
   lines that are yours are the ones the eye lands on. */
.journal-line--other {
  opacity: 0.55;
}
/* The end of the game, in capitals and in colour: gold for the win, red for
   the defeat — which somebody else's win also is, so that line is not dimmed
   as "somebody else's business". */
.journal-line--victory,
.journal-line--defeat {
  opacity: 1;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.journal-line--victory {
  color: #e0a23a;
  border-left-color: #e0a23a;
}
.journal-line--defeat {
  color: #d84a3a;
  border-left-color: #d84a3a;
}
.journal-mark {
  display: flex;
  justify-content: center;
  opacity: 0.8;
}
/* The column is 12px wide and the mark fills it. Anything more detailed than a
   silhouette turns to mush at this size — which is why the set leans on `tent`,
   `trophy` and `ban` rather than on busier drawings. */
.journal-mark svg {
  width: 12px;
  height: 12px;
}
.journal-line b {
  font-weight: normal;
  color: #fff;
}
.journal-word {
  font-weight: 700;
  letter-spacing: 0.5px;
}
.journal-word--war {
  color: #c9483c;
}
.journal-word--peace {
  color: #6fa8c7;
}
.journal-word--ally {
  color: #7fae87;
}
.journal-loss {
  color: #c46a5c;
}
.journal-empty {
  color: #9ea6b3;
  padding: 4px 0 4px 22px;
}
.journal--collapsed .journal-list {
  display: none;
}
/* Diplomacy: the panel of relations (3.3). The wide dialog is this one only —
   Orders and Player have a line of text each and would look lost in it. */
.modal-dialog--wide {
  width: min(720px, calc(100% - 48px));
}
.diplo-row {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) auto;
  column-gap: 10px;
  row-gap: 5px;
  align-items: start;
  padding: 9px 12px;
  background: #151a24;
  border-left: 2px solid #4a5568;
}
.diplo-row > * {
  grid-column: 2;
}
.diplo-row > .diplo-main-pennant {
  grid-column: 1;
  grid-row: 1 / span 4;
  width: 28px;
  height: 32px;
  padding-bottom: 5px;
  filter: drop-shadow(0 1px 1px rgb(0 0 0 / 0.45));
}
.diplo-actions {
  grid-column: 3;
  grid-row: 1 / span 4;
  display: grid;
  gap: 5px;
  align-content: start;
}
.diplo-actions .game-button {
  padding: 5px 10px;
  font-size: 11px;
  white-space: nowrap;
}
.diplo-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  line-height: 1.2;
}
.diplo-name {
  color: #e7e1d3;
}
.diplo-state {
  font-size: 11px;
  letter-spacing: 1px;
}
.diplo-state--war {
  color: #c46a5c;
}
.diplo-state--ally {
  color: #7fae87;
}
.diplo-state--peace {
  color: #9ea6b3;
}
.diplo-note {
  color: #9ea6b3;
  font-size: 11px;
  line-height: 1.3;
}
.diplo-offer {
  color: #e0a23a;
}
.diplo-offer svg {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
}
.diplo-pending {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #e0a23a;
  font-size: 11px;
}
/* One player's own wars and alliances. `font-size: 0` is load-bearing: a line
   break between two chips would otherwise become an anonymous flex item and add
   its own pixels to the gap, so the spacing would depend on how the markup is
   formatted. The label puts the size back. */
.diplo-ties {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0;
  line-height: 1;
}
.diplo-ties + .diplo-ties {
  margin-top: 5px;
}
/* Wide enough for the longest label with its tracking, so the chips of both
   rows stand on one vertical. Sized by content they would not. */
.diplo-ties > b {
  flex: 0 0 76px;
  margin-right: 4px;
  font-size: 11px;
  font-weight: normal;
  letter-spacing: 1px;
}
.diplo-ties--ally > b {
  color: #7fae87;
}
.diplo-ties--war > b {
  color: #c46a5c;
}
/* Relationship lists use the same arms as the main rows, only reduced enough
   to remain secondary to the realm names above. */
.diplo-ties > .diplo-tie-pennant {
  display: grid;
  place-items: center;
  width: 18px;
  height: 20px;
  padding-bottom: 3px;
  flex: 0 0 auto;
  filter: drop-shadow(0 1px 1px rgb(0 0 0 / 0.4));
}
.diplo-tie-pennant--you {
  filter: drop-shadow(0 0 1px #e0a23a) drop-shadow(0 1px 1px rgb(0 0 0 / 0.4));
}
.diplo-row--war {
  border-left-color: #a3352c;
}
.diplo-row--ally {
  border-left-color: #3d6b47;
}
.diplo-row--out {
  opacity: 0.5;
}
.diplo-row--self {
  border-left-color: #e0a23a;
  background: #1a2130;
  margin-bottom: 12px;
}
.diplo-row--self .diplo-name {
  color: #e0a23a;
}
.diplo-others {
  display: grid;
  gap: 6px;
}
.diplo-foot {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid #252e3f;
  color: #9ea6b3;
  font-size: 11px;
}
.diplo-confirm {
  margin: 12px 0;
  padding: 10px 12px;
  background: #251a1a;
  border: 1px solid #6d3029;
}
.diplo-confirm p {
  margin: 0 0 8px;
  color: #e7e1d3;
  font-size: 11px;
  line-height: 1.4;
}
.diplo-confirm-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
.game-button--danger {
  background: #a83a32;
}
.game-button--danger:hover {
  background: #c45248;
}
/* District lines inside a state are a political fact, and the political lens
   is where they are drawn; over the general lens's biome tints they looked
   like seams in the ground. */
[data-layer="borders"]:not([data-lens="political"]) .game-district-border {
  display: none;
}
.game-selection-outline,
.game-hover-outline {
  fill: none;
  pointer-events: none;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.game-selection-outline {
  stroke: #ffd84d;
  stroke-width: 5;
}
.game-hover-outline {
  stroke: #fff;
  stroke-width: 4;
}
/* The army overview (P4). Every army the player has, gathered into the places
   they stand in, with the force as a whole on top.

   It is the ordinary centred dialog and behaves like every other one: the
   backdrop dims, a click outside closes it, Esc closes it. It was briefly given
   a transparent backdrop so the right-hand panel would stay live beside it —
   which cost the only way of closing it a player would look for, and made this
   the one window on the screen that behaved differently. Selecting an army
   still fills that panel and takes the camera there; it is simply read once the
   overview is out of the way.

   The one thing it does differ in is the scroll: head and foot stay put and the
   list moves between them, so the summary and Disband all do not scroll away in
   a realm with thirty armies. That is why padding sits on the parts rather than
   on the dialog. */
.modal-dialog--armies {
  display: flex;
  flex-direction: column;
  width: min(460px, calc(100% - 48px));
  max-height: min(560px, 80%);
  padding: 0;
  overflow: hidden;
}
.modal-dialog--armies .modal-head {
  margin: 0;
  padding: 14px 14px 0;
}
/* With the padding moved onto the parts, the one part that is not there in an
   empty realm — the list — was carrying it, and "no armies" came out flush
   against the frame. */
.modal-dialog--armies .empty {
  padding: 10px 14px 16px;
}
/* The force as one line, and the only thing here that cannot be clicked — so it
   wears no surface, no border and no hover, just type closed by a rule. Every
   row below is a bordered card, which makes "bordered" mean "clickable" without
   anyone having to be told. */
.armies-sum {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 14px 12px;
  border-bottom: 1px solid #252e3f;
}
.armies-sum b {
  color: #e7e1d3;
  font-size: 20px;
  font-weight: normal;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.armies-sum-share {
  color: #e0a23a;
  font-size: 12px;
}
.armies-sum-of {
  margin-left: auto;
  color: #9ea6b3;
  font-size: 11px;
}
.armies-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
}
.armies-card {
  /* Never squeezed to fit — the list scrolls instead. Cards clip their own
     corners, so without this they shrink under their own text as soon as the
     list is longer than the panel. */
  flex: 0 0 auto;
  background: #151a24;
  border: 1px solid #2f3a4d;
  border-radius: 4px;
  overflow: hidden;
}
.armies-card:hover {
  border-color: #4a5568;
}
/* A group is the same card wearing a different accent, and the armies in it are
   ordinary cards — the only thing that makes them a group is sitting inside one.
   Its surface is a step lighter so they read as contained. */
.armies-card--group {
  background: #1a2130;
  border-color: #52697f;
}
/* Colour is spent on selection alone, so it wins wherever it meets the group
   accent. Either a group is selected or one army in it is, never both. */
.armies-card--selected {
  border-color: #e0a23a;
}
.armies-tag {
  color: #8fa8bf;
  font-size: 10px;
  letter-spacing: 1.5px;
}
.armies-members {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0 8px 8px;
}
.armies-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 12px;
  align-items: baseline;
  width: 100%;
  padding: 10px 12px;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
}
.armies-row:hover {
  background: #1a2130;
}
.armies-card--group > .armies-row:hover {
  background: #202a3d;
}
.armies-name {
  color: #e7e1d3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Strength is the column the eye runs down; tabular figures line the digits up
   without a table around them. */
.armies-size {
  color: #e7e1d3;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.armies-note {
  grid-column: 1 / -1;
  margin-top: 3px;
  color: #e0a23a;
  font-size: 11px;
}
.armies-foot {
  padding: 10px 14px;
  border-top: 1px solid #252e3f;
}
/* Built on `.game-button--quiet`, which is already this idiom — dark, muted,
   red on hover. Only the resting text colour is new, so the button reads as
   destructive before it is touched. */
.armies-danger {
  width: 100%;
  color: #c98a80;
  border-color: #6b3a34;
}
.armies-confirm {
  display: grid;
  gap: 6px;
}
.armies-confirm p {
  margin: 0;
  color: #c46a5c;
  font-size: 11px;
  line-height: 1.35;
}
.armies-confirm-row {
  display: flex;
  gap: 6px;
}
.armies-confirm-row .game-button {
  flex: 1;
}
