@property --solar {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.5;
}

:root {
  --bg: #dedede;
  --panel: #ffffff;
  --ink: #03050b;
  --muted: #555555;
  --line: #c1c1c1;
  /* A row rule a quarter of --line; each scope that sets --line sets it again so it follows. */
  --line-faint: color-mix(in srgb, var(--line) 25%, transparent);
  --accent: #ffd24a;
  --on-accent: var(--ink);
  --danger: #d32f2f;
  /* Crest sizes (src/ui/crest.js); a crest is square. The cache draws every
     crest at --crest-profile, so it stays the largest. */
  --crest-list: 38px;
  --crest-chip: 22px;
  --crest-profile: 96px;
  /* An account with no crest: loud, so it reads as something gone wrong. */
  --crest-missing: #ff4fd8;
  /* Paired with INK.costly in src/city/ground_inks.js: the amber of a move that costs morale. */
  --warn: #e39b2d;
  /* The dot on an unread news post, report, or alliance notice. */
  --unread-mark: #ec0d0d;
  /* The ground a settler's portrait stands on, wherever it shows. */
  --portrait-ground: #575b4b;
  /* The title screen's ground: the grassland's green, under the scene while it
     loads and in its place when it cannot. */
  --title-ground: #768130;
  /* The corner of a portrait in a raid's floating card: the attack line's
     commanders and the raider roster's groups. */
  --card-pic-radius: 6px;
  --btn-positive: var(--accent);
  --btn-positive-ink: var(--on-accent);
  --btn-positive-fill: color-mix(in srgb, var(--accent) 55%, white);
  --btn-go: #2e9e5e;
  --btn-go-ink: #ffffff;
  --btn-go-fill: #7fd3a3;
  --btn-start: #2f8fc4;
  --btn-start-ink: #ffffff;
  --btn-start-fill: #8fcdee;
  --btn-negative: #c4453c;
  --btn-negative-ink: #ffffff;
  --btn-negative-fill: #e08a84;
  --btn-neutral: var(--line);
  --btn-neutral-ink: var(--ink);
  --btn-neutral-fill: #a8a8a8;
  --btn-discord: #5865f2;
  --btn-discord-ink: #ffffff;
  --btn-discord-fill: #a5acf8;
  --chrome: color-mix(in srgb, var(--bg) 94%, transparent);
  --chip-well: #cfcfcf;
  --chip-fill: color-mix(in srgb, var(--accent) 30%, var(--chip-well));
  --chip-fill-work: color-mix(in srgb, var(--accent) 52%, var(--chip-well));
  --dim: color-mix(in srgb, var(--ink) 28%, transparent);
  --shadow: 0 8px 24px color-mix(in srgb, var(--ink) 12%, transparent);
  --scroll: color-mix(in srgb, var(--ink) 22%, transparent);
  --scroll-hover: color-mix(in srgb, var(--ink) 38%, transparent);
  --font: system-ui, "Segoe UI", sans-serif;
  --float-width: 720px;
  --chat-dock-tablet: 456px;
  --chat-list: 140px;
  --chat-thread: 500px;
  --chat-dock-landscape: calc(var(--chat-list) + var(--chat-thread));
  --panel-pad: 16px;
  --panel-enter-ms: 400;
  --panel-leave-ms: 200;
  --panel-enter-ease: cubic-bezier(0.87, 0, 0.13, 1); /* inOutExpo */
  --panel-leave-ease: cubic-bezier(0.55, 0.055, 0.675, 0.19);
  --badge-hang: 6px;
  /* How far something that can't be done yet fades: a disabled or blocked
     button or switch, a good the bag has none of, a blocked Build or Plant
     tile. */
  --dim-opacity: 0.5;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  color-scheme: light;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  user-select: none;
  -webkit-user-select: none;
  /* iOS ignores user-scalable=no; this stops double-tap zoom. */
  touch-action: manipulation;
}

input[type="range"] {
  touch-action: none;
}

/* A range slider (.range-bar): a thick rounded bar that fills gold up to its
   knob, a light disc ringed in gold inside the bar. The fill is the knob's
   shadow reaching left, clipped to the bar, so a value code sets fills too.
   The shadow's right edge sits at the knob's centre. */
.range-bar {
  --range-h: 24px;
  --range-fill: var(--accent);
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: var(--range-h);
  padding: 0;
  border: 0;
  border-radius: 99px;
  overflow: hidden;
  background: color-mix(in srgb, var(--text) 14%, transparent);
  cursor: pointer;
}

.range-bar:focus-visible {
  outline: 2px solid var(--range-fill);
  outline-offset: 2px;
}

.range-bar::-webkit-slider-runnable-track {
  height: 100%;
  border: 0;
  background: none;
}

.range-bar::-moz-range-track {
  height: 100%;
  border: 0;
  background: none;
}

.range-bar::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: var(--range-h);
  height: var(--range-h);
  border: 4px solid var(--range-fill);
  border-radius: 50%;
  background: var(--text);
  box-shadow: calc(-100vw - var(--range-h) / 2) 0 0 100vw var(--range-fill);
}

.range-bar::-moz-range-thumb {
  box-sizing: border-box;
  width: var(--range-h);
  height: var(--range-h);
  border: 4px solid var(--range-fill);
  border-radius: 50%;
  background: var(--text);
  box-shadow: calc(-100vw - var(--range-h) / 2) 0 0 100vw var(--range-fill);
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

#play-veil {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: var(--chat-inset);
  z-index: var(--z-veil);
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms linear;
}

#play-veil.is-on {
  opacity: 1;
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  #play-veil {
    transition-duration: 0s;
  }
}

.city-travel-overlay {
  position: absolute;
  inset: 0;
  z-index: var(--z-mid);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  background: #000;
  color: #fff;
  pointer-events: auto;
}

.city-travel-overlay-title {
  font-size: 1.2rem;
  font-weight: 600;
}

.city-travel-overlay-countdown {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.screen-play:not(.city-moving) .city-travel-overlay,
.screen-play.map-open .city-travel-overlay {
  display: none;
}

.screen-play.city-moving .city-play-btns,
.screen-play.city-moving .building-sheet,
.screen-play.city-moving .city-sheet,
.screen-play.city-moving .city-held,
.screen-play.city-moving .city-find-modal,
.screen-play.city-moving .city-family-sheet,
.screen-play.city-moving .city-editor,
.screen-play.city-visiting .city-play-btns,
.screen-play.city-visiting .top-bar,
.screen-play.city-visiting .city-family-sheet,
.screen-play.city-visiting .city-family-bubbles,
.screen-play.city-visiting .city-sheet-collect,
.screen-play.city-visiting .city-sheet-harvest,
.screen-play.city-visiting .city-sheet-manage,
.screen-play.city-visiting .city-sheet-discard,
.screen-play.city-visiting .city-sheet-cancel,
.screen-play.city-visiting .menu-bar-row > button:not(#visit-back, #visit-move-btn, #visit-attack-btn, #armies-btn),
.screen-play.city-visiting:not(.visit-armies) #armies-btn {
  display: none;
}

.city-canvas,
.world-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: var(--z-canvas);
  touch-action: none;
  user-select: none;
  pointer-events: auto;
}

.city-names {
  position: absolute;
  inset: 0;
  z-index: var(--z-names);
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition-property: opacity;
  transition-timing-function: linear;
}

.city-ask {
  position: absolute;
  inset: 0;
  z-index: var(--z-names);
  overflow: hidden;
  pointer-events: none;
}

.city-names.is-on {
  opacity: 1;
}

.city-ask-board,
.city-names-board {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

.map-pins {
  position: absolute;
  inset: 0;
  z-index: var(--z-names);
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition-property: opacity;
  transition-timing-function: linear;
}

.map-pins.is-on {
  opacity: 1;
}

.map-pin {
  position: absolute;
  left: 0;
  top: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--chip-well);
  color: var(--gold-200);
  transform-origin: 0 0;
  visibility: hidden;
  opacity: 0;
  transition-property: opacity;
  transition-timing-function: linear;
}

/* A raider's level, on a pill across the foot of its pin (pin_icons.js). */
.map-pin-level {
  position: absolute;
  left: 50%;
  bottom: -7px;
  transform: translateX(-50%);
  box-sizing: border-box;
  min-width: 18px;
  height: 15px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--surface-0);
  color: var(--text);
  font: 700 11px/15px var(--font);
  text-align: center;
  white-space: nowrap;
}

.map-pin.is-on {
  opacity: 1;
}

.screen-play .map-pin .i {
  width: 62%;
  height: 62%;
  filter: none;
}

.map-pin-hub,
.map-pin-spoke {
  color: var(--gold-ink);
}

.map-pin-hub {
  background: #eb8c1f;
}

.map-pin-spoke {
  background: #ffd133;
}

/* --troop-own, --troop-ally and --troop-enemy are battle/troop_colours.js's, set by pin_icons.js. */
.map-pin-own {
  background: var(--troop-own);
  color: var(--text);
}

.map-pin-ally {
  background: var(--troop-ally);
  color: var(--text);
}

.map-pin-enemy {
  background: var(--troop-enemy);
  color: var(--text);
}

@font-face {
  font-family: "Power Chord";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./powerchord.fb3b9b9418ed.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./source-sans-3-400.03271590a466.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("./source-sans-3-500.731b2f524fd7.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("./source-sans-3-600.7f8a222a36af.woff2") format("woff2");
}

@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("./barlow-condensed-600.4644bc787687.woff2") format("woff2");
}

@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./barlow-condensed-700.2cbc6cbed0a8.woff2") format("woff2");
}

@font-face {
  font-family: "Barlow Semi Condensed";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./barlow-semi-condensed-400.fdb8bfadfbe8.woff2") format("woff2");
}

@font-face {
  font-family: "Barlow Semi Condensed";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("./barlow-semi-condensed-500.133868cbef58.woff2") format("woff2");
}

@font-face {
  font-family: "Barlow Semi Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("./barlow-semi-condensed-600.f795a82ac0eb.woff2") format("woff2");
}

@font-face {
  font-family: "Press Start 2P";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./press-start-2p.06d63aa9c218.woff2") format("woff2");
}

/* Block, not swap: the intertitle types one letter at a time, and a fallback
   face swapping out mid-line reads as a glitch. */
@font-face {
  font-family: "Handwriting";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("./handwriting.d7d92d75934d.woff2") format("woff2");
}

.title-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.5s ease;
}

.title-canvas.is-ready { opacity: 1; }
html.title-live body { background: var(--title-ground); }
html.title-live .title-wordmark { color: #fff; text-shadow: 0 0 14px #000, 0 0 2px #000; }
@media (prefers-reduced-motion: reduce) {
  .title-canvas { transition: none; }
}

.title-canvas[hidden],
.designer-canvas[hidden] {
  display: none !important;
}

.designer-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
}

#ui {
  position: relative;
  z-index: 1;
  height: 100%;
  pointer-events: none;
}

.screen {
  pointer-events: auto;
}

.screen[hidden],
[hidden] {
  display: none !important;
}

.screen-title {
  position: relative;
  /* Visible height, so the bottom menu box rides above the phone keyboard. */
  height: var(--vv-height, 100dvh);
  pointer-events: auto;
  --z-panels: 2;
}

.title-wordmark {
  position: absolute;
  top: max(20px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--float-width), 100%);
  z-index: 1;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  container-type: inline-size;
  font-family: "Power Chord", sans-serif;
  font-weight: 400;
  line-height: 0.95;
  text-align: center;
  color: var(--ink);
  text-shadow: 0 0 14px var(--bg), 0 0 2px var(--bg);
  pointer-events: none;
  --wordmark-line-em: 5.99; /* Power Chord width plus letter spacing. */
  --wordmark-leave: 1s;
}

/* A campaign setting off: the wordmark slides up and fades as the menu
   slides down. */
.title-wordmark.is-leaving {
  transform: translate(-50%, -100%);
  opacity: 0;
  transition:
    transform var(--wordmark-leave) var(--panel-leave-ease),
    opacity var(--wordmark-leave) ease;
}

@media (prefers-reduced-motion: reduce) {
  .title-wordmark.is-leaving { transition: none; }
}

.aspect-portrait .title-wordmark {
  width: calc(100% - 48px);
}

.title-wordmark > span {
  font-size: calc(100cqi / var(--wordmark-line-em));
  letter-spacing: 0.18em;
}

.title-loading {
  position: absolute;
  left: 50%;
  bottom: max(32px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: min(360px, calc(100% - 48px));
  display: flex;
  flex-direction: column;
  gap: 12px;
  z-index: 1;
}

.title-actions,
.title-create,
.title-login,
.title-lost {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.title-actions {
  max-height: 50vh;
  overflow-y: auto;
}

.title-actions button,
.title-lost button,
.title-form-actions button {
  width: 100%;
}

.title-action-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

/* Log in, Register, and Discord stacked on the left; Play fills the right at
   the stack's height. */
.title-primary-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.title-auth-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Play as Guest in title type, its note under it in italic button type. */
.title-play {
  flex-direction: column;
}

.title-play .btn-label {
  font-size: var(--card-title-size);
}

.title-play-note {
  position: relative;
  z-index: 1;
  font-style: italic;
}

.title-terms {
  margin: 0;
  font-size: var(--caption-text-size);
  text-align: center;
}

.title-resume,
.title-account-row {
  min-height: calc((1.2em + 16px) * 2);
}

.title-account-row .btn-label {
  overflow: hidden;
  text-overflow: ellipsis;
}

.title-account-row {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
  gap: 8px;
  align-items: stretch;
}

.title-last-played {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 4px 6px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink);
  font-size: var(--caption-text-size);
  line-height: 1.2;
  text-align: center;
}

.title-create .status:empty,
.title-login .status:empty {
  display: none;
}

.login-terms {
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

.title-form-actions {
  display: grid;
  grid-template-columns: 1fr 3fr;
  gap: 8px;
}

.title-progress {
  position: relative;
  height: 8px;
  background: color-mix(in srgb, var(--ink) 12%, transparent);
  overflow: hidden;
}

.title-progress-fill {
  height: 100%;
  width: 0;
  background: var(--accent);
  animation: title-progress-pulse 1.2s ease-in-out infinite;
}

.title-progress::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 42%;
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, #ffffff 55%, transparent),
    transparent
  );
  animation: title-progress-sheen 1.2s linear infinite;
  pointer-events: none;
}

@keyframes title-progress-sheen {
  from {
    transform: translateX(-120%);
  }
  to {
    transform: translateX(280%);
  }
}

@keyframes title-progress-pulse {
  50% {
    filter: brightness(1.2);
  }
}

#blackout {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms linear;
}

.blackout-load {
  position: absolute;
  left: 50%;
  bottom: max(32px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: min(360px, calc(100% - 48px));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.blackout-status {
  margin: 0;
  color: #fff;
  text-align: center;
  font-size: 0.85rem;
}

#blackout.is-on {
  opacity: 1;
  pointer-events: auto;
}

.intertitle {
  position: fixed;
  inset: 0;
  z-index: 41;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  color: #fff;
  font-family: "Handwriting", cursive;
  font-size: 2rem;
  line-height: 1.5;
  text-align: center;
}

.intertitle-copy {
  margin: 0;
  max-width: var(--float-width, 28rem);
  white-space: pre-wrap;
}

.intertitle-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

@media (prefers-reduced-motion: reduce) {
  #blackout {
    transition-duration: 0s;
  }

  .title-progress-fill,
  .title-progress::after {
    animation: none;
  }
}

.panel h2 {
  margin: 0 0 4px;
  font-size: 1.25rem;
  font-weight: 600;
}

label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.9rem;
}

input,
textarea {
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--line);
  padding: 8px 10px;
  font: inherit;
  /* iOS zooms the page when the focused field is under 16px, and does not reset. */
  font-size: 16px;
  user-select: text;
  -webkit-user-select: text;
}

select {
  font: inherit;
  font-size: 16px;
}

/* The one table of rows (src/ui/list_table.js): muted column names over
   rows split by a line, each led by a bold row header. A table adds only
   rules for its own columns. */
.list-table {
  width: 100%;
  border-collapse: collapse;
}

.list-table th,
.list-table td {
  padding: 8px 6px;
  text-align: left;
  border-bottom: 1px solid var(--line-faint);
}

.list-table thead th {
  color: var(--muted);
  font-size: var(--meta-text-size);
  font-weight: 500;
}

.list-table tbody th {
  font-weight: 600;
}

.password-field {
  display: flex;
  align-items: stretch;
  background: var(--bg);
  border: 1px solid var(--line);
}

.password-field input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
}

.password-field .password-show {
  flex-shrink: 0;
  background: none;
  color: var(--muted);
  padding: 8px 10px;
}

button {
  font: inherit;
  cursor: pointer;
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  padding: 8px 12px;
  -webkit-tap-highlight-color: transparent;
}

button:disabled,
button[aria-disabled="true"] {
  opacity: var(--dim-opacity);
  cursor: default;
}

button:focus {
  outline: none;
}

button:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--ink) 28%, transparent);
  outline-offset: 2px;
}

button:active:not(:disabled):not([aria-disabled="true"]) {
  filter: brightness(1.16);
}

button.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  isolation: isolate;
  user-select: none;
  text-align: center;
}

button.btn.confirm-hold {
  touch-action: none;
}

button.btn.positive {
  background: var(--btn-positive);
  color: var(--btn-positive-ink);
  border: 0;
}

button.btn.go {
  background: var(--btn-go);
  color: var(--btn-go-ink);
  border: 0;
}

button.btn.start {
  background: var(--btn-start);
  color: var(--btn-start-ink);
  border: 0;
}

button.btn.negative {
  background: var(--btn-negative);
  color: var(--btn-negative-ink);
  border: 0;
}

button.btn.neutral {
  background: var(--btn-neutral);
  color: var(--btn-neutral-ink);
  border: 0;
}

button.btn.discord {
  gap: 8px;
  background: var(--btn-discord);
  color: var(--btn-discord-ink);
  border: 0;
}

button.btn.discord .i {
  flex: none;
  width: 20px;
  height: 20px;
}

button.btn.processing:disabled {
  opacity: 1;
}

.btn-fill {
  position: absolute;
  inset: 0;
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
  z-index: 0;
}

button.btn.positive .btn-fill {
  background: var(--btn-positive-fill);
}

button.btn.go .btn-fill {
  background: var(--btn-go-fill);
}

button.btn.start .btn-fill {
  background: var(--btn-start-fill);
}

button.btn.negative .btn-fill {
  background: var(--btn-negative-fill);
}

button.btn.neutral .btn-fill {
  background: var(--btn-neutral-fill);
}

button.btn.discord .btn-fill {
  background: var(--btn-discord-fill);
}

button.btn.armed .btn-fill {
  animation: btn-drain 3s linear forwards;
}

@keyframes btn-drain {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

.btn-label {
  position: relative;
  z-index: 1;
}

/* An icon-only button (an icon prepended, no words) keeps no gap for its label. */
.btn-label:empty {
  display: none;
}

button.btn.holding {
  --btn-shake-x: 0.4px;
  --btn-shake-y: 0.4px;
  --btn-shake-rot: 0.25deg;
  transform-origin: 50% 50%;
  transform-box: border-box;
  background: var(--btn-hold-bg);
  animation: btn-shake 0.1s linear infinite;
}

@keyframes btn-shake {
  0%,
  100% {
    transform: rotate(0deg) translate(0, 0);
  }
  20% {
    transform: rotate(var(--btn-shake-rot)) translate(var(--btn-shake-x), calc(-1 * var(--btn-shake-y)));
  }
  40% {
    transform: rotate(calc(-1 * var(--btn-shake-rot))) translate(calc(-1 * var(--btn-shake-x)), var(--btn-shake-y));
  }
  60% {
    transform: rotate(calc(-0.5 * var(--btn-shake-rot))) translate(var(--btn-shake-x), var(--btn-shake-y));
  }
  80% {
    transform: rotate(var(--btn-shake-rot)) translate(calc(-1 * var(--btn-shake-x)), calc(-1 * var(--btn-shake-y)));
  }
}

button.link {
  background: none;
  color: var(--accent);
  padding: 0;
}

.tabs {
  margin: 0;
  color: var(--muted);
}

.status {
  margin: 0;
  color: var(--danger);
  min-height: 1.2em;
}

.muted {
  color: var(--muted);
  margin: 0;
}

.account-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-list button {
  text-align: left;
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--line);
}

.screen-play {
  position: relative;
  min-height: 0;
  height: var(--vv-height, 100dvh);
  display: grid;
  /* Docked chat is column 1 at full height. HUD lives in the remaining column. */
  grid-template-columns: var(--chat-inset) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "dock top"
    "dock mid"
    "dock menu";
  pointer-events: none;
  --chat-inset: 0px;
  --menu-inset: 8px;
  --z-canvas: 0;
  --z-names: 1;
  --z-mid: 2;
  --z-chrome: 3;
  --z-top: 4;
  --z-panels: 5;
  --z-notifications: 6;
  --z-veil: 8;
  --z-chat: 9;
  --z-toasts: 10;
  --z-editor: 11;
}

html.aspect-portrait .screen-play:has(> #radio-host:not([hidden])) > .play-top-row > .top-bar {
  display: none;
}

/* In portrait a bottom panel reaches the top of the screen: the day clock, the debug chip, and
   the settings and debug buttons would sit over its tabs and close button. */
html.aspect-portrait .screen-play:has(#panels:not([hidden])) .play-top-controls {
  visibility: hidden;
}

html.aspect-tablet .screen-play:not(:is(.find-open, .city-unloading)):has(> #radio-host:not([hidden])) {
  --chat-inset: var(--chat-dock-tablet);
}

html.aspect-landscape .screen-play:not(:is(.find-open, .city-unloading)):has(> #radio-host:not([hidden])) {
  --chat-inset: var(--chat-dock-landscape);
}

.screen-play .chrome {
  pointer-events: auto;
}

.screen-play:has(.panels:not([hidden])) :is(.play-top-hud, .city-play-btns) {
  display: none;
}

/* The account and morale row stacks with the top row, above the top panel's box and dim. The
   panel opens where the row hangs, so the row steps aside. */
.screen-play:has(.top-panel:not([hidden])) .play-top-hud {
  display: none;
}

@property --top-band {
  syntax: "yes | no";
  inherits: false;
  initial-value: no;
}

.play-top-row {
  position: relative;
  grid-area: top;
  z-index: var(--z-notifications);
  min-width: 0;
  display: flex;
  align-items: stretch;
  gap: 12px;
  container: play-top / inline-size;
}

/* Wide rows join the clock to the resources. Portrait keeps it in the corner.
   Once the row can hold that band, the row and the chip row under it stay at
   --play-top-band and sit in the middle of the play column. */
html:not(.aspect-portrait) .play-top-row {
  --top-band: yes;
  width: min(calc(100% - 24px), var(--play-top-band));
  justify-self: center;
}


.top-bar {
  position: relative;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: var(--chrome);
  overscroll-behavior: none;
}

.chips {
  display: flex;
  flex-wrap: nowrap;
  gap: 10px;
  flex: 1;
  min-width: 0;
  cursor: pointer;
  user-select: none;
}

.chips.locked {
  cursor: default;
}

.chip {
  flex: 1;
  min-width: 72px;
  max-width: 128px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: none;
}

.chip-name {
  font-size: 14px;
  text-align: left;
  line-height: 1.2;
  overflow: hidden;
}

.chip-well {
  position: relative;
  height: 32px;
  background: var(--chip-well);
}

.chip-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 0%;
  background: var(--chip-fill);
}

.chip-fill-work {
  background: var(--chip-fill-work);
}

.chip-fill-stalled {
  background: color-mix(in srgb, var(--danger) 55%, var(--chip-well));
}

.chip-qty {
  position: relative;
  z-index: 1;
  height: 32px;
  display: flex;
  align-items: center;
  padding-left: 6px;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
}

/* The account and morale row hangs under the top row. Where the clock and tools float in the
   corner, the cluster stops short of them; the wide band pulls them into the row and frees it. */
.play-top-hud {
  position: absolute;
  top: calc(100% + var(--play-top-gap));
  left: var(--play-top-inset);
  right: var(--play-top-inset);
  z-index: var(--z-chrome);
  display: flex;
  align-items: flex-start;
  gap: 8px;
  pointer-events: none;
}

.play-top-hud .play-left-cluster {
  margin-right: calc(var(--icon-control-size) + 8px + var(--corner-clock-reserve));
}

.play-top-hud .play-left-cluster,
.play-top-controls .play-right {
  pointer-events: none;
}

.play-top-controls {
  position: absolute;
  top: calc(100% + var(--play-top-gap));
  right: var(--play-control-inset);
  z-index: var(--z-chrome);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  pointer-events: none;
}

.play-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  pointer-events: none;
}

.day-clock {
  pointer-events: none;
}

.debug-stack {
  position: fixed;
  top: 50%;
  right: var(--play-top-inset, 12px);
  transform: translateY(-50%);
  z-index: var(--z-chrome);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  pointer-events: none;
}

.debug-chip {
  padding: 8px 12px;
  background: var(--chrome);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 0.85rem;
  white-space: pre;
  text-align: right;
  pointer-events: auto;
}

.debug-transfers {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.debug-transfer-reset,
.debug-map-cache-drop,
.debug-clear-local,
.debug-dialogue-test,
.debug-cards-test,
.debug-noon-midnight {
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  font: inherit;
  line-height: 1;
}

.debug-transfer-reset {
  width: 20px;
  height: 20px;
  padding: 0;
}

.debug-map-cache-drop,
.debug-clear-local,
.debug-dialogue-test,
.debug-cards-test,
.debug-noon-midnight {
  display: block;
  margin-left: auto;
  padding: 3px 6px;
  pointer-events: auto;
}

.debug-panel {
  width: 200px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--chrome);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  pointer-events: auto;
}

.debug-panel[hidden] {
  display: none;
}

.debug-pester {
  width: auto;
  max-width: min(92vw, 520px);
  max-height: 40vh;
  overflow: auto;
  white-space: pre;
  text-align: left;
  font-size: 0.75rem;
  line-height: 1.35;
}


.hud-tools {
  display: flex;
  flex-direction: column;
  gap: 4px;
  pointer-events: auto;
}

.settings-btn {
  width: 36px;
  height: 36px;
}

.settings-btn,
.debug-btn {
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  font-size: 18px;
  line-height: 1;
}

.settings-btn.active {
  border-color: var(--accent);
}

.play-left-cluster {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  pointer-events: none;
}

.play-left-cluster > * {
  pointer-events: auto;
}

.account-chip,
.morale-chip,
.morale-progress {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
}

.account-chip {
  position: relative;
  overflow: visible;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

#account-chip-crest:empty {
  display: none;
}

/* Verify email: where the PIN goes. It carries the last badge of the trail
   to it, and is red until the login's email is verified. */
.account-verify-card {
  position: relative;
  overflow: visible;
}

.account-verify-card.unverified {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 16%, var(--bg));
}

.account-verify-card.unverified .settings-card-head {
  border-bottom-color: var(--danger);
  color: var(--danger);
}

.morale-chip {
  width: 36px;
  height: 36px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  line-height: 1;
}

.morale-progress {
  position: relative;
  flex: 1;
  min-width: 0;
  height: 36px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--chip-well);
  border-color: var(--play-line-strong);
  border-radius: var(--control-radius);
}

.morale-progress .chip-fill {
  left: 0;
}

.morale-chip-copy {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  max-width: 100%;
  padding: 0 8px;
  pointer-events: none;
}

.morale-chip-face {
  flex-shrink: 0;
  font-size: 18px;
  line-height: 1;
}

.morale-chip-label {
  min-width: 0;
  font-size: 14px;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.morale-chip.active {
  border-color: var(--accent);
}

.morale-chip[hidden],
.morale-progress[hidden] {
  display: none;
}

.menu-bar {
  position: relative;
  grid-area: menu;
  z-index: var(--z-notifications);
  min-width: 0;
  display: flex;
  justify-content: center;
  padding: var(--menu-inset);
  background: var(--chrome);
}

html.keyboard-open .menu-bar {
  display: none;
}

.screen-play.menu-hidden .menu-bar {
  display: none;
}

html.keyboard-open .screen-play {
  padding-bottom: 12px;
}

.menu-bar-row {
  display: flex;
  width: var(--float-width);
  max-width: 100%;
  gap: 6px;
}

.aspect-portrait .menu-bar-row {
  width: 100%;
}

.menu-bar button {
  position: relative;
  overflow: visible;
  flex: 1;
  min-width: 64px;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
}

.badge {
  position: absolute;
  top: calc(-1 * var(--badge-hang));
  right: calc(-1 * var(--badge-hang));
  z-index: 1;
  box-sizing: border-box;
  width: 14px;
  height: 14px;
  padding: 0;
  border-radius: 999px;
  background: var(--danger);
  pointer-events: none;
}

.play-middle {
  position: relative;
  grid-area: mid;
  min-width: 0;
  min-height: 0;
}

.radio-host {
  position: absolute;
  inset: 0;
  grid-area: dock;
  z-index: var(--z-chat);
  display: flex;
  pointer-events: none;
}

/* The Radio dock (radio_dock.js): its Chat / Calls strip on top, then the pane it shows. */
.radio-dock {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  width: 100%;
  height: 100%;
  border-right: 1px solid var(--line);
  box-shadow: none;
  background: var(--skin-panel);
}

/* The bottom sheets' tabs (.panel-tabs), standing at the dock's top edge
   rather than floating above it. */
.radio-tabline {
  --panel-tabline-height: 48px;
  flex: none;
  display: flex;
  padding-right: 14px;
  border-bottom: 1px solid var(--line);
  background: var(--skin-accent);
}

.chat-host {
  display: flex;
  flex: 1;
  min-width: 0;
  min-height: 0;
}

/* The Calls pane (panels/calls.js): a box per contact or shelter, raised like a build tile
   with its status edge on the left, green while one of its calls can be fulfilled and amber
   otherwise, and the red badge at its corner while one waits on the player. Its asks and
   gives are the building sheets' term groups. */
.calls-pane {
  padding-block: 12px;
  padding-left: 12px;
}

.call-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.call-box {
  position: relative;
  padding: 10px;
  background: var(--skin-raised);
  border-left: 3px solid var(--skin-accent);
}

.call-box.is-ready {
  border-left-color: var(--ok);
}

.call-box-title {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: var(--tile-title-size);
  line-height: 1.25;
  overflow-wrap: anywhere;
}

/* A contact's answered calls, one line each under its box's calls. */
.call-history-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: 8px;
  border-top: 1px solid var(--line-faint);
}

.call-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 10px;
}

.call-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

.call-listen {
  align-self: flex-start;
  margin-top: 6px;
}

.call-reject {
  margin-inline-end: auto;
}

.aspect-portrait .radio-host {
  display: block;
  grid-area: mid;
  z-index: var(--z-chat);
}

.aspect-portrait .radio-dock {
  position: absolute;
  inset: 0;
  width: auto;
  border-right: 0;
}

.aspect-landscape .chat-list-pane {
  width: var(--chat-list);
  flex: 0 0 var(--chat-list);
}

.aspect-landscape .chat-thread-pane {
  width: var(--chat-thread);
  flex: 0 0 var(--chat-thread);
}

.chat-list-pane,
.chat-thread-pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  height: 100%;
}

.aspect-portrait .chat-host.thread-open .chat-list-pane,
.aspect-tablet .chat-host.thread-open .chat-list-pane {
  display: none;
}

.aspect-portrait .chat-host:not(.thread-open) .chat-thread-pane,
.aspect-tablet .chat-host:not(.thread-open) .chat-thread-pane {
  display: none;
}

.aspect-portrait .chat-list-pane,
.aspect-portrait .chat-thread-pane,
.aspect-tablet .chat-list-pane,
.aspect-tablet .chat-thread-pane {
  width: 100%;
  flex: 1 1 auto;
}

.aspect-landscape .chat-rooms-back {
  display: none;
}

.chat-thread-header {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
}

.chat-thread-header h2 {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  font-size: 1rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The Rooms back (bindBack) lets its unread badge hang past its edge. */
button.chat-rooms-back {
  overflow: visible;
  flex: 0 0 auto;
}

.chat-leave,
.chat-ticket {
  flex: 0 0 auto;
}

/* View ticket stands as tall as the Leave close beside it, as the Back does. */
.screen-play button.btn.chat-ticket {
  min-height: var(--icon-control-size);
}

.chat-rooms {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.chat-room {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  text-align: left;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
}

.chat-room.selected {
  background: var(--row-selected);
}

/* A room's kind, in its own colour: an icon at the left of its row and a word beside its name.
   Where the room list fills the Radio the word sits right of the name; in the narrow landscape list
   it drops under it. */
.chat-room {
  --kind: var(--warn);
  --chat-room-face: 32px;
  border-bottom-color: var(--chat-room-line);
}

.chat-room-global {
  --kind: #b393ff;
}

.chat-room-alliance {
  --kind: #6fa3ff;
}

.chat-room-private {
  --kind: #3fc7b5;
}

/* The room's unread badge hangs on the icon's top right corner. */
.chat-room-face {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  width: var(--chat-room-face);
  height: var(--chat-room-face);
  border: 1px solid color-mix(in srgb, var(--kind) 45%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--kind) 14%, transparent);
  color: var(--kind);
}

.chat-room-text {
  display: flex;
  flex: 1 1 auto;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.chat-room-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-room-label {
  flex: none;
  margin-left: auto;
  font-size: var(--meta-text-size);
  color: var(--kind);
}

.aspect-landscape .chat-room-text {
  flex-direction: column;
  gap: 0;
}

.aspect-landscape .chat-room-label {
  margin-left: 0;
}

.chat-thread {
  --chat-stack-gap: 8px;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: var(--chat-stack-gap);
}

.chat-msg {
  --chat-avatar: var(--crest-list);
  --chat-msg-gap: 8px;
  display: flex;
  align-items: flex-start;
  gap: var(--chat-msg-gap);
  width: 100%;
  user-select: none;
  -webkit-user-select: none;
}

.chat-msg.continued {
  padding-left: calc(var(--chat-avatar) + var(--chat-msg-gap));
  margin-top: calc(-1 * var(--chat-stack-gap));
}

.chat-msg:has(+ .chat-msg.continued) .chat-bubble {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  border-bottom-width: 0;
}

.chat-msg.continued .chat-bubble {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  border-top-style: dashed;
}

.chat-avatar {
  flex: 0 0 var(--chat-avatar);
  padding: 0;
  border: 0;
  line-height: 0;
  border-radius: 0;
  color: var(--skin-panel);
  background: var(--skin-accent);
}

.crest {
  display: block;
  flex: none;
}

.crest-list {
  width: var(--crest-list);
  height: var(--crest-list);
}

.crest-chip {
  width: var(--crest-chip);
  height: var(--crest-chip);
}

.crest-profile {
  width: var(--crest-profile);
  height: var(--crest-profile);
}

.crest-missing {
  background: var(--crest-missing);
}

.chat-bubble {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  padding: 6px 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  user-select: none;
  -webkit-user-select: none;
  white-space: pre-wrap;
  word-break: break-word;
  padding-top: 2px;
}

.chat-bubble.mine {
  background: color-mix(in srgb, var(--accent) 18%, var(--panel));
}

.chat-bubble-top {
  display: flex;
  gap: 8px;
  margin-bottom: 2px;
  align-items: flex-start;
}

.chat-bubble-name,
.chat-bubble-body {
  flex: 1 1 auto;
  min-width: 0;
}

.chat-bubble-name {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--ink);
}

/* The System's and the Developer's names read in their own colour through --text, which the play
   screen's name rule reads. */
.chat-developer {
  --text: var(--developer);
}

.chat-system {
  --text: var(--system);
}

.chat-bubble-age {
  flex: 0 0 auto;
  margin-left: auto;
  font-size: var(--meta-text-size);
  color: var(--muted);
  text-box-trim: trim-start;
  text-box-edge: cap alphabetic;
}

.chat-msg.continued .chat-bubble-top {
  margin-bottom: 0;
}

.chat-bubble.redacted {
  color: var(--muted);
  font-style: italic;
}

.chat-react-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.chat-react-chips:empty {
  display: none;
}

.chat-host button.chat-react-chip {
  display: inline-flex;
  align-items: center;
  min-height: 0;
  min-width: 0;
  padding: 0 5px;
  font-size: 0.85rem;
  line-height: 1.6;
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  border: 0;
  border-radius: 999px;
  color: var(--ink);
  box-shadow: none;
}

.chat-host button.chat-react-chip.mine {
  background: color-mix(in srgb, var(--ink) 16%, transparent);
}

.chat-react-menu {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  max-width: calc(100% - 8px);
  padding: 2px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow);
}

.chat-host .chat-react-menu button {
  min-height: var(--icon-control-size);
  min-width: var(--icon-control-size);
  padding: 0;
  font-size: 1.05rem;
  line-height: 1;
  background: transparent;
  color: inherit;
  border: 0;
  border-radius: 6px;
  box-shadow: none;
}

.chat-host .chat-react-menu button:hover {
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

.chat-host button.chat-react-chip:active,
.chat-host .chat-react-menu button:active {
  filter: none;
}

.chat-typing {
  margin: 0;
  padding: 4px 12px 0;
  font-size: var(--caption-text-size);
  color: var(--muted);
}

.chat-composer {
  flex: 0 0 auto;
  display: flex;
  gap: 6px;
  padding: 8px 12px;
  border-top: 1px solid var(--line);
}

.chat-composer input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 8px;
}

.chat-composer button {
  flex: 0 0 auto;
  padding: 6px 12px;
}

.chat-composer.is-locked input,
.chat-composer.is-locked button {
  opacity: var(--dim-opacity);
}

.top-panel {
  --top-pad: 8px;
  position: absolute;
  inset: 0;
  z-index: var(--z-top);
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding-top: var(--top-pad);
  pointer-events: none;
  overflow: visible;
}

.top-panel-dim {
  position: fixed;
  inset: 0;
  left: var(--chat-inset);
  background: var(--dim);
  opacity: 0;
  pointer-events: none;
  transition: opacity calc(var(--panel-enter-ms) * 1ms) linear;
}

.top-panel.is-modal .top-panel-dim {
  pointer-events: auto;
}

.top-panel.is-open.is-modal .top-panel-dim {
  opacity: 1;
}

.top-panel.is-leaving.is-modal .top-panel-dim {
  opacity: 0;
  transition-duration: calc(var(--panel-leave-ms) * 1ms);
}

.top-panel-box {
  pointer-events: auto;
  position: relative;
  z-index: 1;
  width: var(--float-width);
  max-width: 100%;
  margin: 0 auto;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: 24px 28px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: visible;
  transform: translate3d(0, calc(-100% - var(--top-pad, 8px)), 0);
  transition: transform calc(var(--panel-enter-ms) * 1ms) var(--panel-enter-ease);
}

.top-panel.is-close .top-panel-box {
  max-height: 100%;
  min-height: 0;
}

.top-panel.is-open .top-panel-box {
  transform: none;
}

.top-panel.is-leaving .top-panel-box {
  transform: translate3d(0, calc(-100% - var(--top-pad, 8px)), 0);
  transition-timing-function: var(--panel-leave-ease);
  transition-duration: calc(var(--panel-leave-ms) * 1ms);
}

.top-panel.is-entering .top-panel-box,
.top-panel.is-leaving .top-panel-box {
  pointer-events: none;
}

.aspect-portrait .top-panel {
  --top-pad: 0px;
}

.aspect-portrait .top-panel-box {
  width: 100%;
  max-width: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 16px;
}

.top-panel-header {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  overflow: visible;
}

.top-panel-title {
  flex: 1;
  min-width: 0;
  white-space: normal;
  overflow: visible;
}

.top-panel-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
}

.top-panel.is-close .top-panel-body {
  flex: 1 1 auto;
}

.top-panel.is-collapsed .top-panel-body {
  display: none;
}

.top-panel-dismiss {
  position: relative;
  z-index: 2;
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  padding: 0;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 8px;
}

html:not(.aspect-portrait) .top-panel-dismiss {
  position: absolute;
  right: 28px;
  bottom: 24px;
  transform: translate(33px, 30px);
}

.build-long {
  margin: 0 0 10px;
}

/* A building's descriptions (build.js, build_tile.js): words the author
   wraps in asterisks read bold and amber, as dialogue's do. */
.building-copy :is(em, strong) {
  font-style: normal;
  font-weight: 700;
  color: var(--warn);
}

@media (prefers-reduced-motion: reduce) {
  .top-panel-box,
  .top-panel-dim {
    transition-duration: 0s;
  }
}

.city-play-btns {
  position: absolute;
  left: max(var(--menu-inset), calc((100% - var(--float-width)) / 2));
  bottom: 12px;
  z-index: var(--z-chrome);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

#dialogue {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-chrome);
  display: flex;
  justify-content: center;
  padding: 0 12px;
  pointer-events: none;
}

#dialogue[hidden] {
  display: none;
}

/* The floating menu bar's transparent padding would leave a gap under the bust. */
html:not(.aspect-portrait) .screen-play:not(:is(.city-arranging, .find-open, .city-unloading, .menu-hidden)) #dialogue {
  bottom: -8px;
}

.dialogue-strip {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  width: min(var(--float-width), 100%);
}

.dialogue-strip.is-right {
  flex-direction: row-reverse;
}

.dialogue-bust {
  --dialogue-bust-size: 112px;
  position: relative;
  width: var(--dialogue-bust-size);
  height: var(--dialogue-bust-size);
  flex: 0 0 var(--dialogue-bust-size);
  overflow: hidden;
  transform: translateY(100%);
}

.dialogue-bust.is-up {
  transform: none;
}

.dialogue-bubble {
  --dialogue-emphasis: var(--warn);
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  margin-bottom: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 18px;
  box-shadow: var(--shadow);
  padding: 12px 16px;
  font-size: 16px;
  pointer-events: none;
  visibility: hidden;
}

/* Speech tail toward the bust. */
.dialogue-bubble::before {
  content: "";
  position: absolute;
  left: -7px;
  bottom: 20px;
  width: 12px;
  height: 12px;
  background: inherit;
  border-left: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transform: rotate(45deg);
}

.dialogue-strip.is-right .dialogue-bubble::before {
  left: auto;
  right: -7px;
  border-left: 0;
  border-bottom: 0;
  border-top: 1px solid var(--line);
  border-right: 1px solid var(--line);
}

.dialogue-bubble.is-shown {
  visibility: visible;
}

.dialogue-bust.is-clickable,
.dialogue-bust.is-clickable .royal-plate,
.dialogue-bubble.is-clickable {
  pointer-events: auto;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.dialogue-name {
  color: var(--dialogue-emphasis);
  font-weight: 600;
  margin-bottom: 4px;
}

.dialogue-line {
  white-space: pre-wrap;
}

/* Words the author wraps in asterisks share the speaker's name color. */
.dialogue-line strong {
  color: var(--dialogue-emphasis);
}

/* A choice (dialogue.js pick): the option buttons under the line, the one part of the
   bubble that takes taps. The author's words wrap rather than clip at phone width. */
.dialogue-options {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  pointer-events: auto;
}

.dialogue-options:empty {
  display: none;
}

.dialogue-options .btn {
  min-width: 0;
}

.dialogue-options .btn-label {
  padding: 6px 0;
  line-height: 1.2;
  white-space: normal;
}

.dialogue-options .btn:disabled .btn-label {
  visibility: hidden;
}

.dialogue-live {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.city-sheet,
.map-cell-sheet {
  position: absolute;
  left: 0;
  top: 0;
  z-index: var(--z-mid);
  min-width: 160px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  pointer-events: auto;
}

html.is-pointer-guard .building-sheet,
html.is-pointer-guard .building-sheet *,
html.is-pointer-guard .city-sheet,
html.is-pointer-guard .city-sheet *,
html.is-pointer-guard .city-held,
html.is-pointer-guard .city-held *,
html.is-pointer-guard .map-cell-sheet,
html.is-pointer-guard .map-cell-sheet * {
  pointer-events: none;
}

/* The held building's buttons (hold_bar.js): Cancel, Rotate, and Confirm,
   pinned over the held building as the status card pins. A finger only. */
.city-held {
  position: absolute;
  left: 0;
  top: 0;
  z-index: var(--z-mid);
  display: flex;
  align-items: center;
  gap: 6px;
  pointer-events: auto;
}

/* The held building's line in the top panel: the morale the move makes,
   then, for a mouse, its keys. */
.hold-info {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.hold-info-morale {
  padding: 2px 8px;
  border-radius: 5px;
  background: var(--surface-1);
  border: 1px solid var(--play-line-strong);
  font-family: var(--font-ui);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.hold-info-morale[data-tone="good"] {
  color: var(--ok);
}

.hold-info-morale[data-tone="bad"] {
  color: var(--warn);
}

/* Card and rail form one measured box for pinLeftover. */
.building-sheet {
  --city-sheet-tool-width: 46px;
  --city-sheet-gap: 8px;
  position: absolute;
  left: 0;
  top: 0;
  z-index: var(--z-mid);
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--city-sheet-tool-width);
  grid-template-rows: auto auto;
  gap: 0 var(--city-sheet-gap);
  width: 360px;
  max-width: calc(100% - 24px);
  pointer-events: auto;
  --city-sheet-content-pad: 12px 18px;
}

.city-sheet-tools {
  display: grid;
  grid-row: 1 / 3;
  grid-template-rows: subgrid;
}
.city-sheet-levels {
  display: flex;
  flex-direction: column;
  grid-row: 2;
  align-self: start;
  gap: 4px;
}
:where(.city-sheet-tools:has(.city-sheet-move:not([hidden]))) .city-sheet-levels { padding-top: 4px; }
.city-sheet-head { display: flex; align-items: center; gap: 8px; }

.city-find-modal {
  position: absolute;
  inset: 0;
  z-index: var(--z-mid);
  display: none;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
}

.city-find-modal:not([hidden]) {
  display: flex;
}

/* The game dims toward the edges, so a find sits in a pool of light; blurred where the graphics allow. */
.city-find-dim {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 60% 55% at 50% 50%, #0b121499 0%, #05090af0 100%);
}

html.dim-blur .city-find-dim {
  backdrop-filter: blur(4px);
}

.city-find-sheet {
  position: relative;
  left: auto;
  top: auto;
  z-index: 1;
  min-width: 220px;
}

.screen-play:is(.find-open, .city-unloading) .play-top-row,
.screen-play:is(.find-open, .city-unloading) .play-top-hud,
.screen-play:is(.find-open, .city-unloading) .city-play-btns,
.screen-play:is(.find-open, .city-unloading) .menu-bar,
.screen-play:is(.find-open, .city-unloading) .notifications,
.screen-play:is(.find-open, .city-unloading) #radio-host,
.screen-play:is(.find-open, .city-unloading) .top-panel,
.screen-play:is(.find-open, .city-unloading) .city-family-sheet,
.screen-play:is(.find-open, .city-unloading) #city-sheet {
  display: none;
}

/* An opener draws across the whole screen, over the dimmed game, with nothing boxing it. */
.city-find-panel {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

.city-find-panel[hidden] {
  display: none;
}

.city-find-stage {
  position: absolute;
  inset: 0;
  touch-action: none;
  pointer-events: auto;
  overflow: hidden;
}

/* Where the opener's camera frames its subject; the scene draws past it to the screen's edges. */
.city-find-focus {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(100% - 32px, 960px);
  height: clamp(320px, 62vh, 560px);
  translate: -50% -50%;
  pointer-events: none;
}

.city-find-stage canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  /* A tap on a key or the lock is play, not a link: no tint over the whole stage. */
  -webkit-tap-highlight-color: transparent;
}

button.city-find-leave {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  right: max(12px, env(safe-area-inset-right));
  z-index: 3;
  pointer-events: auto;
}

button.city-find-leave[hidden] {
  display: none;
}

.city-find-deal {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.city-find-cards {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.city-find-cards[hidden] {
  display: none;
}

button.city-find-card {
  position: absolute;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font-family: var(--font-ui);
  perspective: 900px;
  cursor: pointer;
  pointer-events: auto;
  transition: filter 220ms ease;
  -webkit-tap-highlight-color: transparent;
}

button.city-find-card:disabled {
  opacity: 1;
  filter: none;
  pointer-events: none;
  cursor: default;
}

.city-find-card-inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: translate 160ms ease;
}

.city-find-card:not(:disabled):hover .city-find-card-inner,
.city-find-card:focus-visible .city-find-card-inner {
  translate: 0 -6px;
}

.city-find-card:not(:disabled):hover .city-find-card-back,
.city-find-card:focus-visible .city-find-card-back {
  border-color: var(--skin-accent);
}

.city-find-card.is-up .city-find-card-inner {
  transform: rotateY(180deg);
}

.city-find-card-back,
.city-find-card-front {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  box-sizing: border-box;
  padding: 8px;
  backface-visibility: hidden;
}

/* Face down, a card is a panel: the panel fill inside the panel line, a question mark in the accent. */
.city-find-card-back {
  border: var(--panel-border);
  background: var(--surface-1);
  color: var(--skin-accent);
  transition: border-color 140ms linear;
}

.city-find-card-stamp {
  font-family: var(--font-display);
  font-size: 64px;
  font-weight: 700;
  line-height: 1;
}

.city-find-card-sealed {
  margin-top: 6px;
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 3px;
}

/* Face up, a card is a row card standing on end: the raised fill, edged in the item's ink. */
.city-find-card-front {
  border-left: 3px solid var(--find-item);
  background: var(--surface-2);
  transform: rotateY(180deg);
  overflow: hidden;
}

.city-find-card-front::after {
  content: "";
  position: absolute;
  inset: -40%;
  background: linear-gradient(115deg, transparent 35%, #fff7 49%, transparent 62%);
  translate: -100% 0;
  pointer-events: none;
}

.city-find-card.is-up .city-find-card-front::after {
  animation: find-card-sheen 700ms 220ms ease-out both;
}

.city-find-card-icon {
  width: 56px;
  height: 56px;
  color: var(--find-item);
}

.screen-play .city-find-card-icon .i {
  width: 100%;
  height: 100%;
  filter: none;
}

.city-find-card-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
  font-size: var(--caption-text-size);
  font-weight: 600;
  line-height: 1.2;
}

.city-find-card-qty {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.city-find-card::after {
  content: "";
  position: absolute;
  inset: -10px;
  border: 2px solid var(--find-item);
  opacity: 0;
  pointer-events: none;
}

.city-find-card.is-up::after {
  animation: find-card-burst 520ms 240ms ease-out;
}

/* Rarity's palette: anything of a rare or epic item wears is-rare or is-epic
   (item_look.js rarityClass) and reads these. A find card's back matches
   every other card; only the face tells. */
.is-rare {
  --tier-deep: #7a5410;
  --tier-mid: #c8861a;
  --tier-lit: #ffe28a;
  --tier-pale: #fff4c2;
  --tier-fill: #5b4516;
  --tier-glow: #ffc24a;
  --tier-ink: #2a1a04;
}

.is-epic {
  --tier-deep: #4a1d7a;
  --tier-mid: #9a4dff;
  --tier-lit: #dcb3ff;
  --tier-pale: #f6eaff;
  --tier-fill: #3b2258;
  --tier-glow: #b46cff;
  --tier-ink: #1c0833;
}

.city-find-card.is-tier .city-find-card-front {
  border: 2px solid transparent;
  background:
    linear-gradient(var(--tier-fill), var(--surface-2)) padding-box,
    conic-gradient(from var(--tier-spin, 0deg), var(--tier-deep), var(--tier-lit), var(--tier-mid), var(--tier-pale), var(--tier-deep)) border-box;
  box-shadow: 0 0 16px color-mix(in srgb, var(--tier-glow) 40%, transparent);
}

.city-find-card.is-tier.is-up .city-find-card-front {
  animation: find-tier-spin 2.4s linear infinite, find-tier-glow 1.6s ease-in-out infinite alternate;
}

.city-find-card-tag {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg) translateZ(1px);
  backface-visibility: hidden;
  pointer-events: none;
}

/* A rare or epic item's badge: its tier word on a tag in the tier's colours
   (rarityLabel). Its owner places it. */
.tier-badge {
  padding: 3px 9px;
  border-radius: var(--control-radius);
  background: linear-gradient(var(--tier-lit), var(--tier-mid));
  color: var(--tier-ink);
  font-size: 9px;
  font-weight: 900;
  letter-spacing: 2px;
  text-transform: uppercase;
  box-shadow: 0 0 0 2px #0d1212, 0 0 10px color-mix(in srgb, var(--tier-glow) 60%, transparent);
}

.city-find-card-badge {
  position: absolute;
  top: 0;
  left: 75%;
  translate: -50% -50%;
}

.city-find-cards.is-spotlight .city-find-card:not(.is-spotlit) {
  filter: brightness(.4) saturate(.5);
  pointer-events: none;
}

.city-find-card.is-spotlit { z-index: 2; }

.city-find-card.is-tier.is-up::after { animation: none; }

.city-find-card.is-tier.is-landed::after {
  border-color: var(--tier-lit);
  animation: find-tier-burst 700ms ease-out;
}

.city-find-spark {
  position: absolute;
  width: 3px;
  height: 12px;
  margin: -6px 0 0 -1.5px;
  border-radius: 2px;
  background: linear-gradient(#fff, var(--tier-lit) 40%, var(--tier-mid));
  box-shadow: 0 0 6px var(--tier-glow);
  pointer-events: none;
}

.city-find-deal:has(.city-find-cards:not([hidden])) {
  background: color-mix(in srgb, var(--surface-1) 70%, transparent);
  animation: find-reveal-in 350ms ease-out both;
}

html.dim-blur .city-find-deal:has(.city-find-cards:not([hidden])) {
  backdrop-filter: blur(3px);
}

.city-find-trunk-target {
  position: absolute;
  width: 40px;
  height: 40px;
  translate: -50% -50%;
  border: 1px solid #f1cf87b3;
  border-radius: 50%;
  box-shadow: 0 0 20px #e9b96722, inset 0 0 12px #e9b96722;
  pointer-events: none;
}

.city-find-trunk-target::after {
  content: "";
  position: absolute;
  inset: -6px;
  border: 1px solid #f1cf8740;
  border-radius: 50%;
}

.city-find-stage:not([data-phase="idle"]) .city-find-trunk-target { display: none; }
.city-find-stage[data-phase="armed"] canvas { cursor: ns-resize; }
.city-find-stage[data-phase="idle"] canvas { cursor: crosshair; }

.city-find-trunk-force {
  position: absolute;
  bottom: 13px;
  left: 50%;
  translate: -50% 0;
  width: 140px;
  height: 3px;
  background: #b0bba329;
  opacity: 0;
  pointer-events: none;
}

.city-find-trunk-force span {
  display: block;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, #b4bd96, #f5c15f, #ff8051);
  box-shadow: 0 0 10px #e99c6266;
  transform-origin: left;
}

.city-find-stage[data-phase="armed"] .city-find-trunk-force,
.city-find-stage[data-phase="committed"] .city-find-trunk-force { opacity: 1; }
@keyframes find-card-burst {
  0% { opacity: .9; transform: scale(.8); }
  100% { opacity: 0; transform: scale(1.3); }
}
@keyframes find-card-sheen {
  to { translate: 100% 0; }
}
@keyframes find-reveal-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@property --tier-spin {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@keyframes find-tier-spin {
  to { --tier-spin: 360deg; }
}
@keyframes find-tier-glow {
  from { box-shadow: 0 0 10px color-mix(in srgb, var(--tier-glow) 27%, transparent); }
  to { box-shadow: 0 0 28px color-mix(in srgb, var(--tier-glow) 67%, transparent); }
}
@keyframes find-tier-burst {
  0% { opacity: 1; transform: scale(.9); box-shadow: 0 0 30px var(--tier-glow); }
  100% { opacity: 0; transform: scale(1.6); box-shadow: 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .city-find-card.is-up::after,
  .city-find-card.is-up .city-find-card-front::after,
  .city-find-deal:has(.city-find-cards:not([hidden])),
  .city-find-card.is-tier.is-landed::after { animation: none; }
  .city-find-card.is-tier.is-up .city-find-card-front { animation: find-tier-glow 1.6s ease-in-out infinite alternate; }
  .city-find-card-inner { transition: none; }
}

button.city-find-close {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  min-width: 140px;
  pointer-events: auto;
  transform: translate(-50%, 28px);
}

button.city-find-close[hidden] {
  display: none;
}

.city-sheet-title,
.map-cell-sheet-title {
  font-weight: 600;
}

.city-family-sheet-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.city-family-sheet-look {
  position: relative;
  width: 72px;
  height: 72px;
  flex: 0 0 72px;
  overflow: hidden;
  background: #f6f1e5;
}

.city-family-bubbles {
  position: absolute;
  inset: 0;
  z-index: var(--z-names);
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition-property: opacity;
  transition-timing-function: linear;
}

.city-family-bubbles.is-on {
  opacity: 1;
}

.city-family-bubble {
  position: absolute;
  left: 0;
  top: 0;
  width: 12px;
  height: 12px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  overflow: hidden;
  background: #f6f1e5;
  pointer-events: auto;
  box-shadow: var(--shadow);
  transform-origin: 50% 100%;
}

.city-family-bubble::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: 50%;
  border: 1px solid #000;
  pointer-events: none;
}

.city-family-bubble.is-founding::after {
  border: 1px solid var(--gold-400);
}

.city-family-bubble canvas {
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
  pointer-events: none;
}

.screen-play.city-arranging .city-family-bubbles {
  display: none;
}

.city-sheet-sub,
.city-sheet-row {
  white-space: pre-line;
}

.city-sheet-row {
  color: var(--muted);
  font-size: var(--meta-text-size);
}

.city-sheet-energy {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--meta-text-size);
  font-variant-numeric: tabular-nums;
}

.city-sheet-energy .city-sheet-energy-track {
  flex: 1;
  min-width: 60px;
  margin-top: 0;
}

.city-sheet-energy-track {
  height: 8px;
  margin-top: 6px;
  background: color-mix(in srgb, var(--line) 70%, transparent);
  overflow: hidden;
}

.city-sheet-energy-fill {
  height: 100%;
  width: 0;
}

.city-sheet-energy-fill.green {
  background: #16a34a;
}

.city-sheet-energy-fill.red {
  background: #dc2626;
}

.city-sheet-craft-out {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-weight: 600;
  margin: 6px 0 4px;
}

.city-sheet-held {
  display: flex;
  align-items: center;
  gap: 8px;
  font-variant-numeric: tabular-nums;
}

.city-sheet-held .i {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--held-ink);
}

.city-sheet-held-full {
  margin-left: auto;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--alert);
}

/* A detail taller than its sheet scrolls, so its actions stay reachable. */
#build-detail,
#plant-detail {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

#plant-panel.panel-pane {
  overflow: hidden;
}

/* Green while stock covers the quantity, amber once a cost runs short. */
#plant-slider {
  --range-fill: var(--ok);
  width: 100%;
  margin: 6px 0;
}

#plant-slider.short {
  --range-fill: var(--accent);
}

#plant-start.blocked {
  opacity: var(--dim-opacity);
}

/* A recipe's own view: its picture and name, the quantity slider, then what
   it takes beside what it makes as the recipe list's pills, and the time to
   complete beside the start button. Upgrade and Expand share its
   head for the rung they start. */
.detail-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0 14px;
  font-family: var(--font-display);
  font-size: var(--card-title-size);
  font-weight: 700;
}

.detail-head-icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--text) 8%, transparent);
}

.detail-head-icon .i {
  width: 28px;
  height: 28px;
}

.term-label {
  font-weight: 600;
  font-size: 0.85rem;
}

/* Stats tiles sit two to a row. */
.term-list.stat-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}

/* A Stats tile that changes: what it was, faint, before what it becomes. */
.formula-was {
  color: var(--muted);
  font-weight: 500;
}

/* A weapon system's reach against the farthest any fires: the dead zone it
   can't fire into hatched red, the ground it reaches gold, a tick at each
   edge. */
.reach-bar {
  position: relative;
  margin-top: 10px;
  padding-bottom: 16px;
}

.reach-track {
  position: relative;
  display: block;
  height: 10px;
  border-radius: 5px;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  box-shadow: inset 0 0 0 1px var(--line);
  overflow: hidden;
}

.reach-dead,
.reach-live {
  position: absolute;
  top: 0;
  bottom: 0;
}

.reach-dead {
  left: 0;
  background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--alert) 60%, transparent) 0 3px, transparent 3px 6px);
}

.reach-live {
  border-radius: 5px;
  background: linear-gradient(var(--gold-200), var(--gold-500));
}

.reach-ticks {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 14px;
}

.reach-tick {
  position: absolute;
  color: var(--muted);
  font-size: var(--caption-text-size);
  font-variant-numeric: tabular-nums;
}

.reach-tick.is-min {
  transform: translateX(-50%);
}

.reach-tick.is-max {
  transform: translateX(-100%);
}

/* How a weapon system fares against each target class, a chip each: strong
   green, weak yellow, a class it can't hit red and struck through. */
.matchups {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
  margin-top: 10px;
}

.matchup {
  display: flex;
  flex-direction: column;
  padding: 5px 9px;
  border-radius: 8px;
  background: var(--chip-well);
  box-shadow: inset 0 0 0 1px var(--line);
  line-height: 1.15;
}

.matchup-class {
  font-weight: 600;
  font-size: 0.85rem;
}

.matchup-state {
  color: var(--muted);
  font-size: var(--caption-text-size);
}

.matchup[data-state="strong"] {
  background: color-mix(in srgb, var(--ok) 16%, var(--chip-well));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ok) 55%, transparent);
}

.matchup[data-state="strong"] .matchup-state {
  color: var(--ok-text);
}

.matchup[data-state="weak"] {
  background: color-mix(in srgb, var(--warn) 14%, var(--chip-well));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 55%, transparent);
}

.matchup[data-state="weak"] .matchup-state {
  color: var(--warn);
}

.matchup[data-state="misses"] {
  background: color-mix(in srgb, var(--alert) 14%, var(--chip-well));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--alert) 55%, transparent);
}

.matchup[data-state="misses"] .matchup-class {
  text-decoration: line-through;
}

.matchup[data-state="misses"] .matchup-state {
  color: var(--alert);
}

/* A weapon's stats (weapon_stats_view.js) on the Floor plan: its range and
   damage a second in a line, its reach, and its matchups. In a Build tile
   they span the tile under its words. */
.weapon-stats-line {
  color: var(--muted);
  font-size: var(--caption-text-size);
  font-variant-numeric: tabular-nums;
}

.screen-play .inv-tile > .weapon-stats {
  grid-column: 1 / -1;
}

#plant-detail .plant-quantity {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: baseline;
}

.plant-count {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Two columns of formula pills in labelled groups, a group hidden while it
   has none: the recipe view, and Build, Upgrade, and Expand. */
.term-columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  margin-top: 10px;
}

.aspect-portrait .term-columns:not(.building-columns) {
  grid-template-columns: minmax(0, 1fr);
}

.aspect-portrait .building-columns .formula-text {
  min-width: 0;
}

.aspect-portrait .building-columns :is(.formula-qty, .formula-name) {
  white-space: normal;
  overflow-wrap: anywhere;
}

.term-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 6px;
}

.term-group:not([hidden]) ~ .term-group {
  margin-top: 12px;
}

.term-list .formula-term {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
}

.plant-detail-actions {
  justify-content: space-between;
  margin-top: 18px;
}

#plant-start {
  flex: 0 0 auto;
}

.plant-time {
  flex: 1 1 auto;
  align-self: center;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.city-sheet-collect,
.map-cell-sheet-prospect,
.map-cell-sheet-construct,
.map-cell-sheet-mine,
.map-cell-sheet-summon,
.map-cell-sheet-move-city,
.map-cell-sheet-recall,
.map-cell-sheet-recall-all,
.map-cell-sheet-view {
  width: 100%;
  margin-top: 8px;
}

.map-cell-sheet-patch {
  white-space: pre-wrap;
}

.map-cell-sheet-leave-text {
  font-size: 0.85rem;
  margin-top: 8px;
}

.map-cell-sheet-leave-row {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

.map-cell-sheet-leave-row button {
  flex: 1;
  min-width: 0;
}

.mine-status,
.mine-stock,
.mine-occupancy,
.mine-construct-stock {
  color: var(--muted);
  font-size: var(--meta-text-size);
}

.mine-prefs {
  display: flex;
  gap: 8px;
  margin: 8px 0;
}

.mine-pref {
  flex: 1;
  min-width: 0;
}

.mine-error {
  color: var(--danger);
  font-size: var(--meta-text-size);
}

.mine-construct-costs-line {
  flex: 1;
}

.mine-construct-cost.is-short {
  color: var(--danger);
}

.mine-construct-error {
  color: var(--danger);
  font-size: var(--meta-text-size);
}

.mine-construct-field {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
}

.mine-construct-field input[type="range"] {
  flex: 1;
}

.map-cell-sheet-error,
.map-cell-sheet-tax,
.map-cell-sheet-patch,
.map-cell-sheet-block,
.map-cell-sheet-family,
.map-cell-sheet-status,
.map-cell-sheet-city-name {
  color: var(--muted);
  font-size: var(--meta-text-size);
  margin-top: 6px;
}

/* Logs and scrap: icon, a bar against the map's richest cell, the amount. */
.map-cell-sheet-stock {
  display: grid;
  grid-template-columns: max-content minmax(96px, 1fr) max-content;
  align-items: center;
  gap: 6px 8px;
  margin-top: 6px;
}

.map-cell-sheet-stock[hidden] {
  display: none;
}

.map-cell-sheet-stock-row {
  display: contents;
}

.screen-play .map-cell-sheet-stock .i {
  width: 20px;
  height: 20px;
}

.map-cell-sheet-stock-qty {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.map-cell-sheet-occupants {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}

.map-cell-sheet-royal {
  display: flex;
  align-items: center;
  gap: 8px;
}

.map-cell-sheet-bust {
  position: relative;
  width: 56px;
  height: 56px;
  flex: 0 0 56px;
  overflow: hidden;
  background: #f6f1e5;
}

.map-cell-sheet-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.map-cell-sheet-name {
  font-weight: 600;
}

/* A raider city's loot: each group's label, then a small chip per item,
   wrapping to the sheet's width. Potential loot (.map-cell-sheet-odds) is
   a line per item instead, its chance per win at the right. */
.map-cell-sheet-loot,
.map-cell-sheet-odds {
  margin-top: 4px;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.map-cell-sheet-loot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 8px;
}

.map-cell-sheet-odds {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 2px 12px;
}

.map-cell-sheet-odds > .map-cell-sheet-loot-label {
  grid-column: 1 / -1;
}

.map-cell-sheet-odds > .map-cell-sheet-loot-item {
  min-width: 0;
}

.map-cell-sheet-odds-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.map-cell-sheet-odds-name + span {
  color: var(--muted);
}

.map-cell-sheet-odds-chance {
  text-align: right;
}

.map-cell-sheet-loot-label {
  color: var(--muted);
}

.map-cell-sheet-loot-item {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

.screen-play .map-cell-sheet-loot-item .i {
  width: 15px;
  height: 15px;
  color: var(--loot-ink);
  filter: drop-shadow(0 1px 0 #000a);
}

.map-cell-sheet-skel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}

.map-cell-sheet-skel-line {
  height: 12px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--muted) 28%, transparent);
  animation: map-cell-skel 1s ease-in-out infinite;
}

.map-cell-sheet-skel-line.short {
  width: 55%;
}

@keyframes map-cell-skel {
  50% { opacity: 0.45; }
}

.city-sheet-collect.blocked {
  opacity: var(--dim-opacity);
}

.upgrade-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 8px 6px;
  margin: 0 -8px;
}

.upgrade-table th {
  text-align: left;
  font-weight: 600;
  font-size: 0.85rem;
}

.upgrade-table td {
  width: 50%;
  vertical-align: top;
}

.upgrade-table.is-single td {
  width: 100%;
}

.upgrade-table tr.staff button {
  min-width: 2.75rem;
  margin-left: 8px;
}

.upgrade-cell {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--panel) 50%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--line) 50%, var(--panel));
  font-size: 0.9rem;
}

.upgrade-cell.short {
  background: color-mix(in srgb, var(--btn-negative) 50%, var(--panel));
  color: var(--ink);
  border-color: color-mix(in srgb, var(--btn-negative) 50%, var(--panel));
}

.upgrade-cell-text {
  flex: 1;
  min-width: 0;
}

.upgrade-check {
  margin-left: auto;
  flex: none;
}

.morale-tail {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

.morale-tail .upgrade-check {
  margin-left: 0;
}

.morale-delta {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.upgrade-go {
  flex: none;
}

#upgrade-start,
#field-expand-start {
  width: 100%;
  margin-top: 12px;
}

#upgrade-start.blocked,
#field-expand-start.blocked,
#build-start.blocked,
#floor-plan-action.blocked,
#floor-plan-save.blocked {
  opacity: var(--dim-opacity);
}

.screen-play .manage-depleted {
  margin: 0;
  padding: 8px 11px;
  border: 1.5px solid var(--alert);
  border-radius: 8px;
  background: color-mix(in srgb, var(--alert) 12%, transparent);
  color: var(--alert);
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 15px;
  line-height: 1.3;
}

.build-detail-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

#build-start {
  flex: 1 1 auto;
}

.screen-play.city-arranging .building-sheet,
.screen-play.city-arranging .city-sheet,
.screen-play.city-arranging .city-find-modal,
.screen-play.city-arranging .city-family-sheet {
  display: none;
}

/* The notification slot (notifications.js): the play column's bottom right, its right edge on
   the menu bar row's. A card with an anchor gets --notification-right and --notification-bottom
   measured from its button and the menu bar's top edge. Floor chrome on its left (Edit and Build
   in portrait) narrows it to --notification-room beside that chrome. The slot clips its card
   --notification-pad below where the card rests, the menu bar's top edge, so a card rises from
   behind what is under it and falls back behind it. A card swiped sideways (portrait) leaves by
   --notification-leave-x instead, off the screen. A flung card leaves at its fling's speed:
   --notification-leave-ease linear over the --notification-leave-ms its distance takes. */
.notifications {
  position: absolute;
  right: var(--notification-right, max(var(--menu-inset), calc((100% - var(--float-width)) / 2)));
  bottom: var(--notification-bottom, var(--notification-pad, 8px));
  z-index: var(--z-toasts);
  max-width: min(var(--notification-max, 380px), calc(100% - 16px), var(--notification-room, 100%));
  clip-path: inset(-100vh -100vw calc(-1 * var(--notification-pad, 8px)) -100vw);
  pointer-events: none;
}

.notification {
  position: relative;
  isolation: isolate;
  margin: 0;
  padding: 8px 12px;
  pointer-events: auto;
  touch-action: none;
  transform: translate3d(0, calc(100% + var(--notification-pad, 8px)), 0);
  transition: transform calc(var(--panel-enter-ms) * 1ms) var(--panel-enter-ease);
}

.notifications.is-open .notification {
  transform: none;
}

.notifications.is-leaving .notification {
  transform: translate3d(var(--notification-leave-x, 0), var(--notification-leave-y, calc(100% + var(--notification-pad, 8px))), 0);
  transition-timing-function: var(--notification-leave-ease, cubic-bezier(.4, 0, 1, 1));
  transition-duration: var(--notification-leave-ms);
}

.notifications.is-entering .notification,
.notifications.is-leaving .notification {
  pointer-events: none;
}

/* The fill clips to the card's corners; the drain runs on its ::before. */
.notification-fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
}

.notification-fill::before {
  content: "";
  position: absolute;
  inset: 0;
  transform-origin: left center;
}

.notification.positive .notification-fill::before {
  background: var(--btn-positive-fill);
}

.notification.neutral .notification-fill::before {
  background: var(--btn-neutral-fill);
}

.notification.negative .notification-fill::before {
  background: var(--btn-negative-fill);
}

.notification.is-holding .notification-fill::before {
  animation: notification-drain var(--notification-hold-ms, 3000ms) linear forwards;
}

@keyframes notification-drain {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

.notification-body {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 8px;
}

.notification.is-shake {
  animation: notification-shake var(--notification-shake-ms, 250ms) ease-in;
}

@keyframes notification-shake {
  0%,
  100% {
    translate: 0 0;
  }
  20% {
    translate: var(--notification-shake, 5.5px) 0;
  }
  40% {
    translate: calc(-1 * var(--notification-shake, 5.5px)) 0;
  }
  60% {
    translate: calc(0.6 * var(--notification-shake, 5.5px)) 0;
  }
  80% {
    translate: calc(-0.4 * var(--notification-shake, 5.5px)) 0;
  }
}

.notification-icon {
  flex: 0 0 auto;
  display: flex;
  width: 22px;
  height: 22px;
  align-self: start;
}

.notification-icon[hidden] {
  display: none;
}

.notification-icon .i {
  width: 100%;
  height: 100%;
}

.notification-text {
  flex: 1 1 auto;
  min-width: 0;
}

.notification-count {
  flex: 0 0 auto;
  font-weight: 600;
}

.notification-action {
  flex: 0 0 auto;
  background: transparent;
  color: inherit;
  padding: 0;
  font-weight: 600;
}

.city-editor {
  position: absolute;
  inset: 0;
  z-index: var(--z-mid);
  pointer-events: none;
}

.city-editor-bar {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: var(--z-chrome);
  display: flex;
  gap: 8px;
  pointer-events: auto;
}

.screen-play.city-arranging .top-bar,
.screen-play.city-arranging .menu-bar,
.screen-play.city-arranging .play-left-cluster,
.screen-play.city-arranging .account-chip,
.screen-play.city-arranging .city-play-btns,
.screen-play.city-arranging .day-clock {
  display: none;
}

.screen-play.map-open .city-play-btns,
.screen-play.map-open .building-sheet,
.screen-play.map-open .city-sheet,
.screen-play.map-open .city-find-modal,
.screen-play.map-open .city-family-sheet,
.screen-play.map-open .city-family-bubbles,
.screen-play.map-open .city-editor {
  display: none;
}

.screen-play:not(.map-open) .map-cell-sheet {
  display: none;
}

/* A scrolling list of catalogue tiles (build_tile.js): Plant's one grid, or
   Build's headed sections, each its own grid. The padding leaves the corner
   badge room to hang. */
.build-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  gap: 18px;
  padding: calc(var(--badge-hang) + 10px) calc(var(--badge-hang) + 10px) 10px 10px;
}

/* Rows hold each tile's full height, so a short panel scrolls the list
   instead of squeezing the seed strip and name plate out of the tiles. */
.build-grid {
  display: grid;
  grid-template-columns: repeat(var(--tile-cols), minmax(0, 1fr));
  grid-auto-rows: max-content;
  align-content: start;
  justify-content: start;
  gap: var(--tile-gap);
  --tile-cols: 1;
  --tile-gap: 8px;
}

/* A row: the big icon, then the building's details. The icon stands as
   tall as the name over two rows of need pills, so a second row never
   grows the tile. A tile too narrow for two pill columns beside the icon
   puts a smaller icon beside the name and the pills under both. */
.build-tile {
  container: build-tile / inline-size;
  --build-name-size: var(--tile-title-size);
  --build-name-line: 1.25;
  --build-needs-gap: 6px;
  --build-glyph-size: calc(var(--build-name-size) * var(--build-name-line) + 2 * (var(--build-needs-gap) + var(--term-height)));
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  width: 100%;
  min-width: 0;
  padding: 0;
  overflow: visible;
  text-align: left;
  z-index: 0;
  transition:
    border-color 140ms linear,
    opacity 140ms linear;
}

.screen-play .build-tile {
  color: var(--text);
  font-family: var(--font-ui);
  font-weight: 600;
  box-shadow: none;
  text-shadow: none;
  filter: none;
  border: 0;
  border-left: 3px solid var(--skin-accent);
  background: var(--skin-raised);
  padding: 10px;
}

/* Whole-pixel columns. A fractional track puts the 1px border on a half
   pixel. A WebView without round() skips this block and keeps the
   fractional columns and full width above. */
@supports (width: round(down, 100%, 1px)) {
  .build-grid {
    grid-template-columns: repeat(
      var(--tile-cols),
      round(down, (100% - (var(--tile-cols) - 1) * var(--tile-gap)) / var(--tile-cols), 1px)
    );
  }

  .build-tile {
    width: round(down, 100%, 1px);
  }
}

/* Clipped to the inside of the tile's border, so the name plate doesn't
   paint over its corners. */
.build-tile-face {
  position: relative;
  display: flex;
  overflow: hidden;
  border-radius: var(--control-radius);
  pointer-events: none;
  flex-direction: row;
  align-items: flex-start;
  gap: 8px;
}

/* A blocked tile remains clickable to show its details. */
.build-tile.blocked {
  opacity: .9;
}

.build-panel .build-tile.blocked {
  opacity: 1;
}

.screen-play .build-tile.ready {
  border-left-color: var(--ok);
}

.screen-play .build-tile.ready .build-tile-glyph {
  color: var(--ok-text);
}

@media (hover: hover) {
  button.build-tile:hover {
    border-color: var(--accent);
  }

  button.build-tile.blocked:hover {
    opacity: 0.8;
  }
}

.build-tile-glyph {
  display: grid;
  place-items: center;
  color: var(--text);
  background: transparent;
  width: var(--build-glyph-size);
  aspect-ratio: 1;
  flex: none;
}

.screen-play .build-tile-glyph .i {
  height: auto;
  aspect-ratio: 1;
  width: 70%;
}

/* Building details beside the glyph. */
.build-tile-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
  flex: 1;
  border: 0;
  padding: 0;
  background: transparent;
}

.build-tile-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 600;
  white-space: normal;
  font-size: var(--build-name-size);
  line-height: var(--build-name-line);
}

/* A tile's needs, as the pills its detail sheet shows, in even columns,
   each pill's text in its middle so they line up down a column. They sit
   on the panel's colour, set into the raised tile. */
.build-tile-needs {
  align-self: stretch;
  margin: var(--build-needs-gap) 0 0;
}

.build-tile-need-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: var(--build-needs-gap);
}

.build-tile-need-list > .formula-term {
  grid-template-columns: auto minmax(0, 1fr) auto;
}

/* Pills set into a raised box (a build tile, a call box) sit on the panel's colour. A need
   already met steps back, edge and all, so the short ones lead. */
.term-inset > .formula-term {
  background: var(--surface-1);
}

.term-inset.term-needs > .formula-term:not(.is-short) {
  opacity: 0.5;
}

/* What a call gives the player is blue; one storage has no room for stays red. */
:where(.call-gives) > .formula-term {
  --term-tone: var(--info-deep);
  --term-icon: var(--info);
}

.build-tile-need-list :is(.formula-qty, .formula-name) {
  overflow: hidden;
  text-overflow: ellipsis;
}

@container build-tile (max-width: 440px) {
  .build-tile-face {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 10px;
  }

  .build-tile-copy {
    display: contents;
  }

  .build-tile-glyph {
    --build-glyph-size: 56px;
  }

  .build-tile-desc,
  .build-tile-needs,
  .build-tile-sub {
    grid-column: 1 / -1;
  }
}

.build-tile-desc {
  max-width: 100%;
  color: var(--muted);
  font-weight: 500;
  font-size: var(--meta-text-size);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.build-tile-desc p {
  margin: 0;
}

.build-tile-sub {
  max-width: 100%;
  color: var(--muted);
  font-weight: 600;
  font-size: var(--caption-text-size);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* A machine's few recipes list as full-width rows. */
.build-grid.is-rows {
  grid-template-columns: minmax(0, 1fr);
}

/* A term is a raised tile with the play skin's status edge on its left,
   like a build tile or a sheet card, and its icon in a darker box, set into
   the pill, filling its height against that edge: --term-tone colours the edge (unset, it is the
   pill's own colour), --term-icon the icon and --term-qty the amount. */
.formula-term {
  display: inline-grid;
  grid-template-columns: auto auto;
  align-items: center;
  column-gap: 8px;
  padding: 0 10px 0 0;
  border-radius: var(--control-radius);
  border-left: 3px solid var(--term-tone, transparent);
  background: var(--surface-2);
}

.formula-icon {
  display: grid;
  place-items: center;
  align-self: stretch;
  width: var(--term-height);
  min-height: var(--term-height);
  border-radius: var(--control-radius);
  background: color-mix(in srgb, #000 22%, transparent);
  color: var(--term-icon, var(--text));
}

.formula-icon .i {
  width: 22px;
  height: 22px;
}

.formula-text {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}

.formula-qty {
  color: var(--term-qty, inherit);
  font-weight: 700;
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.formula-name {
  color: var(--muted);
  font-size: var(--caption-text-size);
  white-space: nowrap;
}

.formula-term.is-out {
  --term-tone: var(--ok);
  --term-icon: var(--ok-text);
}

.formula-term.is-out.is-improvement {
  --term-tone: var(--info-deep);
  --term-icon: var(--info);
}

/* An output a craft may not pay has a dashed edge, and its odds
   right-aligned in a cell of their own behind a dashed rule. */
.formula-term.is-out.is-chance {
  border-left-style: dashed;
}

.formula-odds {
  align-self: stretch;
  display: grid;
  align-content: center;
  justify-items: end;
  box-sizing: border-box;
  width: 3.6rem;
  margin: 0 -10px 0 0;
  padding: 0 10px 0 8px;
  border-left: 1.5px dashed color-mix(in srgb, var(--ok) 55%, transparent);
  font-weight: 700;
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A cost or requirement (paintNeeds) has a green edge and icon while met
   and amber ones while short; anywhere else short is red. Only the edge and
   icon carry the state. */
.term-needs {
  --term-short: var(--warn);
}

:where(.term-needs) > .formula-term {
  --term-tone: var(--ok);
  --term-icon: var(--ok-text);
}

.formula-term.is-short {
  --term-tone: var(--term-short, var(--danger));
  --term-icon: var(--term-tone);
}

/* What a later level brings (Manage's seatLater): amber, both lines small. */
.formula-term.is-later {
  --term-tone: var(--warn);
  --term-icon: var(--warn);
  --term-qty: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, var(--surface-2));
}

.formula-term.is-later .formula-name {
  color: var(--warn);
}

.formula-term.is-later .formula-qty {
  font-size: var(--caption-text-size);
}

.panels {
  position: absolute;
  inset: 0;
  z-index: var(--z-panels);
  overflow: visible;
}

.panels.is-entering,
.panels.is-leaving {
  overflow: hidden;
}

.dim {
  position: fixed;
  inset: 0;
  left: var(--chat-inset);
  background: var(--dim);
  opacity: 0;
  pointer-events: auto;
  transition: opacity calc(var(--panel-enter-ms) * 1ms) linear;
}

.panels.is-open .dim {
  opacity: 1;
}

.panels.is-leaving .dim {
  opacity: 0;
  transition-duration: calc(var(--panel-leave-ms) * 1ms);
}

:is(.top-panel-dim, .dim) {
  transition:
    opacity calc(var(--panel-enter-ms) * 1ms) linear,
    backdrop-filter calc(var(--panel-enter-ms) * 1ms) ease-out;
}

html.dim-blur:not(.aspect-portrait) :is(.top-panel-dim, .dim) {
  backdrop-filter: blur(0px);
}

html.dim-blur:not(.aspect-portrait) .top-panel.is-open.is-modal .top-panel-dim,
html.dim-blur:not(.aspect-portrait) .panels.is-open .dim {
  backdrop-filter: blur(3px);
}

html.aspect-portrait :is(.top-panel-dim, .dim) {
  transition-property: opacity;
}

@media (prefers-reduced-motion: reduce) {
  :is(.top-panel-dim, .dim) {
    transition-duration: 0s;
  }
}

.bottom-panel,
.menu-box {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--panel-pad) 0;
  pointer-events: none;
  overflow: visible;
}

.aspect-portrait .bottom-panel,
.aspect-portrait .menu-box {
  padding: 0;
  --panel-pad: 0px; /* unitless 0 makes calc(100% + var(--panel-pad)) invalid */
}

.bottom-panel.panel-fill .panel {
  flex: 1 1 auto;
  min-height: 0;
}

.panel {
  pointer-events: auto;
  position: relative;
  width: var(--float-width);
  max-width: 100%;
  max-height: 100%;
  margin: 0 auto;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 24px 28px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: visible;
  transform: translate3d(0, calc(100% + var(--panel-pad) + var(--panel-exit-clearance, 0px)), 0);
  transition: transform calc(var(--panel-enter-ms) * 1ms) var(--panel-enter-ease);
  background: var(--panel);
  box-shadow: var(--shadow);
}

.panels.is-open .panel {
  transform: none;
}

.panels.is-leaving .panel {
  transform: translate3d(0, calc(100% + var(--panel-pad) + var(--panel-exit-clearance, 0px)), 0);
  transition-timing-function: var(--panel-leave-ease);
  transition-duration: calc(var(--panel-leave-ms) * 1ms);
}

.panels.is-entering .panel,
.panels.is-leaving .panel {
  pointer-events: none;
}

.panels.is-snap .dim,
.panels.is-snap .panel {
  transition-duration: 0s;
}

.aspect-portrait .panel {
  width: 100%;
  max-width: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 16px;
}

/* Its look is the close button's (.close-btn). */
.panel-close {
  position: relative;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .dim,
  .panel,
  .designer-step,
  .designer-step.is-in {
    transition-duration: 0s;
  }

  .build-tile {
    transition: none;
  }
}

.panel-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
  overflow: hidden;
}

#build-panel .panel-body,
#manage-panel .panel-body {
  flex: 1 1 auto;
}

#alliance-view-panel .panel-body {
  overflow: visible;
}

#account-panel .panel-body {
  flex: 1 1 auto;
}

.account-stack {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.account-stack[hidden] {
  display: none;
}

.account-head {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* Edit crest sits under the crest, on your own profile only. */
.account-crest-col {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.account-name {
  margin: 0;
  overflow-wrap: anywhere;
}

.account-about {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.account-about.is-empty {
  color: var(--muted);
}

.account-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
}

.account-form[hidden] {
  display: none;
}

.account-form textarea {
  resize: vertical;
}

.account-form-row,
.account-form-foot {
  display: flex;
  align-items: center;
  gap: 8px;
}

.account-form-row input {
  flex: 1;
  min-width: 0;
}

.account-count {
  margin-right: auto;
  color: var(--muted);
  font-size: var(--meta-text-size);
}

.account-count.is-over {
  color: var(--danger);
}

/* The account panel's tables (account.js, account_options.js): a row
   header, its value, and its action at the right edge. An edit opens its
   form in the value cell, or in a full-width row under it. */
.account-table td:nth-child(2) {
  width: 100%;
  overflow-wrap: anywhere;
}

.account-table td:nth-child(3),
.account-switch-table td:nth-child(3) {
  text-align: right;
  white-space: nowrap;
}

/* The address and its Needs-style verified mark, centred on one line. */
.account-email {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 12px;
  row-gap: 4px;
}

.account-login-row {
  display: flex;
  gap: 8px;
}

.account-login-row > :first-child {
  flex: 1;
}

.account-login-row > :last-child {
  flex: 2;
}

.account-switch {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.account-switch-table td:nth-child(2) {
  width: 100%;
  text-align: right;
  color: var(--muted);
  font-size: var(--meta-text-size);
}

.account-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.account-actions > * {
  flex: 1;
  min-width: 0;
}

.account-view-meta {
  margin: 0;
  color: var(--muted);
  font-size: var(--meta-text-size);
}

/* Zero specificity: a classed button keeps its own skin. */
:where(.panel-body) button {
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--line);
}

/* A name that opens its owner reads as gold text, not a button. */
button.account-name-link {
  background: none;
  border: 0;
  padding: 0;
  color: var(--accent);
  font: inherit;
  cursor: pointer;
  text-align: left;
}

button.account-name-link:hover,
button.account-name-link:focus-visible {
  text-decoration: underline;
}

/* A card in a panel list, in a building tile's look: a raised fill and a gold left edge.
   News, notices, alliances, feed, armies, summons, reports, inventory tiles, settings sections.
   A tone recolours the edge: info is something new for the player to act on, ok a battle won,
   alert a battle lost. Each owner keeps its own layout. */
.row-card {
  padding: 8px 10px;
  border: 0;
  border-left: 3px solid var(--accent);
  border-radius: var(--panel-radius-inner);
  background: var(--surface-2);
  color: var(--ink);
}

.row-card[data-tone="info"] {
  border-left-color: var(--info);
}

.row-card[data-tone="ok"] {
  border-left-color: var(--ok);
}

.row-card[data-tone="alert"] {
  border-left-color: var(--alert);
}

button.row-card {
  width: 100%;
  text-align: left;
}

button.row-card:hover:not(:disabled) {
  border-left-color: var(--text);
}

.row-card.selected,
button.row-card.selected:hover {
  background: var(--row-selected);
  border-color: var(--accent);
}

.row-card-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.row-card-top {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.row-card-title {
  flex: 1;
  min-width: 0;
}

.row-card-age {
  flex: none;
  margin-left: auto;
  color: var(--muted);
  font-size: var(--meta-text-size);
  white-space: nowrap;
}

.row-card-sub {
  color: var(--muted);
  font-size: var(--meta-text-size);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.news-body-col {
  flex: 1;
  min-height: 0;
}

.news-article {
  flex: 1 1 auto;
  min-height: 0;
  background: var(--bg);
  color: var(--ink);
  border-radius: var(--panel-radius-inner);
  overflow: auto;
  overscroll-behavior: contain;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.news-article h3,
#news-headline {
  margin: 0;
  color: var(--accent);
  font-size: 1.15rem;
  font-weight: 700;
}

.news-summary {
  margin: 0;
  color: var(--muted);
}

.markdown {
  color: var(--ink);
  font-size: 16px;
  line-height: 1.45;
}

.markdown h1,
.markdown h2,
.markdown h3 {
  margin: 0.4em 0 0.2em;
  color: var(--ink);
  font-weight: 700;
}

.markdown h1 {
  font-size: var(--card-title-size);
  color: var(--accent);
}

.markdown h2 {
  font-size: 20px;
}

.markdown h3 {
  font-size: 18px;
}

.markdown p,
.markdown ul,
.markdown ol,
.markdown blockquote {
  margin: 0 0 0.6em;
}

.markdown ul,
.markdown ol {
  padding-left: 1.4em;
}

.markdown blockquote {
  padding-left: 12px;
  border-left: 3px solid var(--accent);
  font-style: italic;
}

.markdown code {
  font-family: ui-monospace, monospace;
  font-size: 0.9em;
}

.markdown a {
  color: var(--accent);
}

.news-list-scroll {
  height: 220px;
  max-height: 220px;
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 0 0 220px;
  /* The list sits at the bottom of the sheet; the open article takes whatever is above it. */
  margin-top: auto;
}

.news-article,
.news-list-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--scroll) transparent;
}

.news-article::-webkit-scrollbar,
.news-list-scroll::-webkit-scrollbar {
  width: 8px;
}

.news-article::-webkit-scrollbar-track,
.news-list-scroll::-webkit-scrollbar-track {
  background: transparent;
}

.news-article::-webkit-scrollbar-thumb,
.news-list-scroll::-webkit-scrollbar-thumb {
  background: var(--scroll);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: 999px;
}

.news-article::-webkit-scrollbar-thumb:hover,
.news-list-scroll::-webkit-scrollbar-thumb:hover {
  background-color: var(--scroll-hover);
}

.news-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-right: 8px;
}

/* The Field Manual's list (panels/field_manual.js): section headings over their entry rows, in
   the pane's own scroll. */
.manual-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.title-mark {
  width: 10px;
  height: 10px;
  margin-top: 7px;
  flex: 0 0 10px;
  border-radius: 8px;
  background: var(--unread-mark);
}

#alliance-panel .panel-body {
  flex: 1 1 auto;
  overflow: auto;
}

.alliance-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
}

#alliance-out {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

#alliance-create-wrap {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

#alliance-create-wrap[hidden] {
  display: none;
}

.alliance-create {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.alliance-create[hidden] {
  display: none;
}

.alliance-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.alliance-actions-end {
  justify-content: flex-end;
}

.alliance-actions .btn {
  flex: 0 0 auto;
  width: auto;
}

.alliance-browse {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.alliance-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.alliance-row-title {
  font-weight: 600;
}

.alliance-row-sub,
.alliance-names {
  color: var(--muted);
  font-size: var(--meta-text-size);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.alliance-member-who {
  display: flex;
  align-items: center;
  gap: 10px;
}

.alliance-member-table .alliance-member-role,
.alliance-member-table .alliance-member-tools {
  width: 1%;
  white-space: nowrap;
}

.alliance-member-table td.alliance-member-role {
  color: var(--muted);
  font-size: var(--meta-text-size);
}

.alliance-member-table .alliance-member-tools {
  text-align: right;
}

.alliance-member-table select {
  padding: 4px 8px;
}

.alliance-summary {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.alliance-summary-name {
  margin: 0;
  overflow-wrap: anywhere;
}

/* An alliance's own words: a well with the quote rule, signed by its leader. */
.alliance-quote {
  margin: 0;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--surface-well);
  border-left: 3px solid var(--accent);
  border-radius: var(--panel-radius-inner);
}

.alliance-quote-text {
  margin: 0;
  font-style: italic;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.alliance-quote-by {
  color: var(--muted);
  font-size: var(--meta-text-size);
  text-align: right;
}

.alliance-view-actions {
  margin-top: auto;
}

.alliance-view-actions .btn.positive {
  margin-left: auto;
}

#alliance-in {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.panel-tabs {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
  min-width: 0;
  padding: 0;
  overflow: visible;
}

.panel-tabs[hidden] {
  display: none;
}

.panel-tab {
  position: relative;
  overflow: visible;
  flex: 1;
  min-width: 0;
}

.panel-tab[hidden] {
  display: none;
}

.panel-pane {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.panel-pane[hidden] {
  display: none;
}

.alliance-edit {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.alliance-edit[hidden] {
  display: none;
}

#alliance-notices-view {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

#alliance-notices-view[hidden] {
  display: none;
}

#alliance-notice-create-open {
  align-self: flex-start;
  width: auto;
}

.alliance-notice-article {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  background: var(--bg);
  color: var(--ink);
  border-radius: var(--panel-radius-inner);
  overflow: auto;
  overscroll-behavior: contain;
  padding: 16px 16px 44px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.alliance-notice-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

#alliance-notice-headline {
  margin: 0;
  flex: 1;
  min-width: 0;
  color: var(--ink);
  font-size: 1.15rem;
  font-weight: 700;
}

.alliance-notice-head .row-card-age {
  font-size: var(--caption-text-size);
}

#alliance-notice-delete {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 36px;
  height: 36px;
  min-width: 36px;
  min-height: 36px;
  padding: 0;
  background: transparent;
  color: var(--ink);
  border: 0;
  font-size: 18px;
  line-height: 1;
}

.alliance-notice-list-scroll {
  height: auto;
  max-height: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 0 1 auto;
}

.alliance-notice-article,
.alliance-notice-list-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--scroll) transparent;
}

.alliance-notice-article::-webkit-scrollbar,
.alliance-notice-list-scroll::-webkit-scrollbar {
  width: 8px;
}

.alliance-notice-article::-webkit-scrollbar-track,
.alliance-notice-list-scroll::-webkit-scrollbar-track {
  background: transparent;
}

.alliance-notice-article::-webkit-scrollbar-thumb,
.alliance-notice-list-scroll::-webkit-scrollbar-thumb {
  background: var(--scroll);
  background-clip: padding-box;
  border: 2px solid transparent;
  border-radius: 999px;
}

.alliance-notice-article::-webkit-scrollbar-thumb:hover,
.alliance-notice-list-scroll::-webkit-scrollbar-thumb:hover {
  background-color: var(--scroll-hover);
}

.alliance-notice-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-right: 8px;
}

#alliance-notice-form.alliance-notice-compose {
  flex: 1 1 auto;
  min-height: 0;
}

#alliance-notice-form.alliance-notice-compose label {
  flex: 1 1 auto;
  min-height: 0;
}

#alliance-notice-body {
  flex: 1 1 auto;
  min-height: 12em;
}

#alliance-panel.alliance-notices-read .panel,
#alliance-panel.alliance-notices-read .alliance-body,
#alliance-panel.alliance-notices-read #alliance-in,
#alliance-panel.alliance-notices-read #alliance-tab-notices,
#alliance-panel.alliance-notices-read #alliance-notices-view,
#alliance-panel.alliance-notices-read .alliance-notice-article {
  flex: 0 1 auto;
}

#alliance-panel.alliance-notices-read .panel-body {
  overflow: hidden;
}

#alliance-panel.alliance-notices-read #alliance-tab-notices {
  overflow: hidden;
}

#alliance-panel.alliance-notices-read .alliance-notice-list-scroll {
  height: 220px;
  max-height: 220px;
  flex: 0 0 220px;
}

#alliance-panel.alliance-notices-compose .panel,
#alliance-panel.alliance-notices-compose .alliance-body,
#alliance-panel.alliance-notices-compose #alliance-in,
#alliance-panel.alliance-notices-compose #alliance-tab-notices,
#alliance-panel.alliance-notices-compose #alliance-notice-form {
  flex: 1 1 auto;
}

#alliance-panel.alliance-notices-compose .panel-body {
  overflow: hidden;
}

#alliance-panel.alliance-notices-compose #alliance-tab-notices {
  overflow: hidden;
}

.alliance-member {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.alliance-feed {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.alliance-matrix-table td,
.alliance-matrix-table thead th:not(:first-child) {
  text-align: center;
}

.alliance-matrix-cell {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
}

.screen-play .alliance-matrix-cell .i {
  position: absolute;
  top: 50%;
  left: calc(100% + 6px);
  width: 14px;
  height: 14px;
  color: var(--muted);
  filter: none;
  transform: translateY(-50%);
}

html.aspect-portrait,
html.aspect-portrait body {
  font-size: 14px;
}

.aspect-portrait .top-bar {
  padding: 4px 8px 6px;
}

.aspect-portrait .chips {
  width: 100%;
  gap: 4px;
}

.aspect-portrait .chip {
  min-width: 0;
  max-width: none;
  gap: 1px;
}

.aspect-portrait .chip .chip-name {
  font-size: 11px;
}

.aspect-portrait .chip .chip-well,
.aspect-portrait .chip .chip-qty {
  height: 22px;
  font-size: 11px;
}

.aspect-portrait .chip .chip-qty {
  padding-left: 4px;
}

.aspect-portrait .play-top-hud {
  left: 8px;
}

.aspect-portrait .debug-chip {
  padding: 4px 8px;
  font-size: 11px;
}

.aspect-portrait .debug-panel {
  width: 168px;
  padding: 8px 10px;
  font-size: 11px;
}

.aspect-portrait .dialogue-bust {
  --dialogue-bust-size: 128px;
}

.dialogue-bust.is-robot {
  --dialogue-bust-size: 88px;
}

@media (prefers-reduced-motion: reduce) {
  .dialogue-bust {
    transform: none;
  }
}

.aspect-portrait .settings-btn {
  width: 32px;
  height: 32px;
}

.aspect-portrait .settings-btn,
.aspect-portrait .debug-btn {
  font-size: 16px;
}

.aspect-portrait .account-chip {
  padding: 4px 8px;
  font-size: 13px;
}

.aspect-portrait .morale-chip {
  width: 32px;
  height: 32px;
  font-size: 16px;
}

.aspect-portrait .morale-progress {
  height: 32px;
}

.aspect-portrait .morale-chip-face {
  font-size: 16px;
}

.aspect-portrait .morale-chip-label {
  font-size: 11px;
}

.aspect-portrait .city-play-btns {
  left: var(--menu-inset);
}

/* In portrait, Edit and Build step aside while a dialogue plays, so its strip has the full width. */
html.aspect-portrait .play-middle:has(> #dialogue:not([hidden])) > .city-play-btns {
  display: none;
}

.screen-designer {
  min-height: 100%;
  height: 100dvh;
  pointer-events: none;
  background: transparent;
}

.screen-designer .panels {
  pointer-events: none;
}

.screen-designer .panel {
  pointer-events: auto;
}

button.secondary {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
}

.royal-slot {
  position: relative;
  width: 180px;
  height: 260px;
  overflow: hidden;
  cursor: pointer;
  flex: 0 0 auto;
  background: #f6f1e5;
}

.royal-plate {
  position: absolute;
  top: 0;
  left: 0;
  image-rendering: pixelated;
  pointer-events: none;
}

.designer-tabs {
  display: flex;
  align-items: flex-end;
  flex: 0 0 auto;
  width: var(--float-width);
  max-width: 100%;
  height: var(--panel-tab-selected-height);
  margin: 0 auto;
  pointer-events: auto;
}

.aspect-portrait .designer-tabs {
  width: 100%;
}

.designer-editors {
  display: grid;
  flex: 0 1 auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.designer-pane {
  grid-area: 1 / 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  overflow: hidden;
}

.designer-pane.is-off {
  visibility: hidden;
  pointer-events: none;
}

.designer-actions {
  display: grid;
  grid-template-columns: 1fr 3fr;
  gap: 8px;
  flex: 0 0 auto;
  padding-top: 8px;
}

.designer-actions button {
  width: 100%;
}

.screen-designer .designer-step {
  transition: transform 500ms ease-in;
}

.screen-designer .designer-step.is-out {
  transform: translate3d(0, calc(100% + var(--panel-pad)), 0);
}

.screen-designer .designer-step.is-in {
  transform: none;
  transition-duration: 300ms;
  transition-timing-function: ease-out;
}

.designer-lead-picks {
  margin: 8px 0 4px;
}

.designer-names-line {
  margin: 8px 0 0;
  line-height: 1.55;
}

.designer-name {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 700;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.designer-name-input {
  display: inline;
  width: auto;
  max-width: 14rem;
  margin: 0;
  padding: 0 2px;
  border: 0;
  border-bottom: 2px solid var(--accent);
  background: transparent;
  font: inherit;
  font-weight: 700;
  color: var(--accent);
}

.designer-child-controls {
  --designer-check: 32px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  margin: 12px auto 0;
}

.child-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  width: 100%;
}

.child-sex {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}

.child-sex .btn {
  flex: 0 0 auto;
  min-height: var(--designer-check);
  box-sizing: border-box;
}

.child-toolbar #child-reroll {
  flex: 0 0 auto;
  margin-left: auto;
  width: auto;
}

.child-inherit {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.child-inherit-label {
  width: 13rem;
}

.child-inherit-pick {
  width: calc((100% - 13rem) / 5);
}

.child-inherit th,
.child-inherit td {
  padding: 6px 8px;
  text-align: center;
  vertical-align: middle;
}

.child-inherit thead th {
  font-weight: 500;
}

.child-inherit thead th:first-child,
.child-inherit .child-inherit-who {
  text-align: left;
  font-weight: 500;
  white-space: nowrap;
}

input:where([type="checkbox"]) {
  appearance: none;
  -webkit-appearance: none;
  display: inline-grid;
  place-items: center;
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--accent);
  border-radius: 3px;
  background: var(--bg);
  vertical-align: middle;
  cursor: pointer;
}

input:where([type="checkbox"]:checked) {
  background: var(--accent);
}

input:where([type="checkbox"]:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

input:where([type="checkbox"]:disabled) {
  cursor: not-allowed;
}

input:where([type="checkbox"])::after {
  content: "";
  width: 5px;
  height: 9px;
  margin-top: -2px;
  border: solid var(--on-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0);
}

input:where([type="checkbox"]:checked)::after {
  transform: rotate(45deg) scale(1);
}

.child-inherit input[type="checkbox"] {
  width: var(--designer-check);
  height: var(--designer-check);
  margin: 0 auto;
}

.child-inherit input[type="checkbox"]::after {
  width: 9px;
  height: 16px;
  margin-top: -3px;
  border-width: 0 3px 3px 0;
}

.trait-col {
  width: 100%;
  max-width: 100%;
  flex: 0 1 auto;
  overflow-x: hidden;
  overflow-y: visible;
}

.trait-table {
  --trait-control-h: 32px;
  width: 100%;
  max-width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

.trait-table th {
  text-align: left;
  font-weight: 500;
  white-space: nowrap;
  width: 7.25rem;
  padding: 5px 12px 5px 0;
  vertical-align: middle;
}

.trait-table td {
  padding: 5px 0;
  vertical-align: middle;
}

.trait-pair {
  display: flex;
  gap: 8px;
  align-items: center;
}

.trait-pair > * {
  flex: 1;
  min-width: 0;
}

.trait-pair .trait-swatches {
  flex: 0 0 auto;
}

.trait-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
}

.trait-stack .trait-picks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
}

.trait-stack .trait-swatches {
  width: 100%;
  min-width: 0;
}

.trait-stack .trait-swatches-wide .trait-swatch,
.panel-body .trait-stack .trait-swatches-wide .trait-swatch {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}

.trait-table tr:has(.trait-stack) th {
  vertical-align: top;
}

.trait-swatches {
  display: flex;
  flex-wrap: nowrap;
  gap: 4px;
  align-items: stretch;
  height: var(--trait-control-h);
}

.trait-swatches-fill {
  width: 100%;
}

.trait-swatch,
.panel-body .trait-swatch {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  height: var(--trait-control-h);
  margin: 0;
  padding: 0;
  border: 1px solid color-mix(in srgb, #000 22%, var(--swatch, var(--line)));
  border-radius: var(--control-radius);
  background: var(--swatch, var(--bg));
  color: inherit;
  display: grid;
  place-content: center;
  cursor: pointer;
}

.trait-swatches-fill .trait-swatch,
.panel-body .trait-swatches-fill .trait-swatch {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
}

.trait-swatches-wide .trait-swatch,
.panel-body .trait-swatches-wide .trait-swatch {
  flex: 0 0 auto;
  width: calc(2 * var(--trait-control-h));
}

.trait-swatch:checked::after,
.panel-body .trait-swatch:checked::after {
  content: "✓";
  font-size: calc(var(--trait-control-h) * 0.62);
  font-weight: 700;
  line-height: 1;
  color: #fff;
  text-shadow: 0 1px 2px #000;
  border: 0;
  transform: none;
  width: auto;
  height: auto;
  margin: 0;
}

.trait-swatch:checked,
.panel-body .trait-swatch:checked {
  border-color: var(--selected-line);
  box-shadow: inset 0 0 0 2px var(--selected-line);
}

.trait-picks {
  display: flex;
  gap: 6px;
  width: 100%;
}

.trait-picks .trait-pick {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
}

.trait-table input,
.trait-table select {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

.trait-table select {
  height: var(--trait-control-h);
  padding: 0 8px;
}

#armies-panel .panel-body,
#family-panel .panel-body,
#inventory-panel .panel-body,
#morale-panel .panel-body,
#settings-panel .panel-body,
#summon-panel .panel-body {
  flex: 1 1 auto;
  min-height: 0;
}

#armies-panel .panel-body,
#family-panel .panel-body,
#inventory-panel .panel-body,
#morale-panel .panel-body,
#summon-panel .panel-body {
  overflow: auto;
  overscroll-behavior: contain;
}

#morale-title {
  margin: 0;
}

#settings-panel .panel-pane {
  overscroll-behavior: contain;
}

.settings-body {
  gap: 20px;
}

/* A settings pane's controls, and the help below them, stand on the panel
   itself, no card around them. */
.settings-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.settings-card {
  padding: 0;
}

.settings-report {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.report-form,
.report-compose {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.report-form h3 {
  margin: 0;
}

.report-note {
  margin: 0;
  color: var(--muted);
  font-size: var(--caption-text-size);
}

.report-board {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Every Reports row is one frame: what happened's icon, the body, and the clock time top right
   with a dot while it is new. */
.report-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  column-gap: 10px;
  align-items: start;
}

.report-lead {
  display: flex;
  width: 22px;
  height: 22px;
}

.report-lead .i {
  width: 100%;
  height: 100%;
}

.report-main {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  line-height: 1.4;
}

.report-corner {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--caption-text-size);
  line-height: 1.4;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.report-dot {
  margin-top: 0;
}

/* What a report gained and lost (report_nodes.js itemNode), as a building's term tiles set into
   the card (term-inset): gained blue, lost red. A rare or epic roll wears its tier's colours
   (the palette is-rare or is-epic sets). */
.report-items {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.report-items:empty {
  display: none;
}

:where(.report-items) > .formula-term {
  --term-tone: var(--info-deep);
  --term-icon: var(--info);
}

:where(.report-items) > .formula-term.is-lost {
  --term-tone: var(--danger);
  --term-icon: var(--danger);
}

:where(.report-items) > .formula-term:is(.is-rare, .is-epic) {
  --term-tone: var(--tier-mid);
  --term-icon: var(--tier-lit);
  background: color-mix(in srgb, var(--tier-fill) 75%, var(--surface-2));
}

/* A battle report's result word, in its tone: green won, red lost (report_nodes.js lineNode). */
.report-result {
  font-weight: 700;
  letter-spacing: 0.04em;
}

.report-result[data-tone="ok"] {
  color: var(--ok);
}

.report-result[data-tone="alert"] {
  color: var(--alert);
}

/* A report row's one action: Replay on a battle's, View on a ticket's, Find a new location on a
   bare stand's Action (report_nodes.js). */
.report-action {
  align-self: flex-start;
}

/* A ticket status's colour, read by the tracker board's status chips (tracker/board.js). */
.tracker-status-accepted {
  --status: #3d8bd9;
}

.tracker-status-in_progress {
  --status: var(--warn);
}

.tracker-status-pending_release {
  --status: #3fae5a;
}

.tracker-status-released {
  --status: #1fa58f;
}

.tracker-status-closed {
  --status: var(--btn-negative);
}

/* Filter chips (Logbook reports, alliance list): a row of small toggles; a pressed one wears the selected gold
   from the shared selected rule by the menu bar. The top padding leaves a chip's corner badge room to
   hang inside the scrolling pane. */
.filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: var(--badge-hang);
}

.filter-chips[hidden] {
  display: none;
}

.filter-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--control-height);
  padding: 0 12px;
  border-radius: var(--control-radius);
  border: 1px solid var(--play-line-strong);
  background: transparent;
  color: var(--text);
  font-size: 0.85rem;
}

.filter-chip .i {
  width: 16px;
  height: 16px;
}

.list-heading {
  margin: 6px 0 0;
}


.report-sent {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}

.report-sent p {
  margin: 0;
}

.report-actions {
  display: flex;
  justify-content: flex-end;
}

.settings-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 6px 14px;
  border-bottom: 1px solid var(--line);
}

#settings-audio-reset {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 4px 10px;
  font-size: 12px;
}

.settings-card-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 16px 14px;
}

.settings-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}

.settings-title {
  font-size: 0.9rem;
}

.settings-row .settings-title {
  flex: 1 1 8rem;
  min-width: 8rem;
}

.settings-fps-readout {
  color: var(--muted);
  font-size: var(--caption-text-size);
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.settings-check-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(3, minmax(40px, auto));
  grid-auto-flow: column;
  gap: 8px 10px;
  margin-top: 4px;
}

.settings-check-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.settings-check {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: color-mix(in srgb, var(--panel) 55%, var(--bg));
  font-size: 0.9rem;
  cursor: pointer;
}

.settings-check:has(input:checked) {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  background: color-mix(in srgb, var(--accent) 8%, var(--bg));
}

.settings-check:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.settings-check:has(input:disabled) {
  cursor: not-allowed;
}

.settings-stack {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.settings-langs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.settings-language-note {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: var(--meta-text-size);
}

#settings-langs button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--control-height);
  padding: 0 12px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--panel) 55%, var(--bg));
}

#settings-langs button::after {
  content: "✓";
  font-weight: 700;
  visibility: hidden;
}

#settings-langs button[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}

#settings-langs button[aria-pressed="true"]::after {
  visibility: visible;
}

#settings-langs button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.settings-dead {
  opacity: 0.45;
}

.settings-janky {
  color: #8b5a2b;
}

.settings-sexy,
.settings-maxed {
  color: #b44cff;
}

.settings-muted {
  color: #e05050;
}

.inv-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.inv-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* The Goods and Troops tabs' tiles (inventory_tile.js): each its icon beside its name over its
   amount, as many to a row as fit, with a thin bar of segments under them. Goods come in a
   section per kind of good. */
.screen-play .inv-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.screen-play .inv-section-title {
  margin: 0;
}

/* A section's title, as the Build sheet heads its sections: inventory sections, and a list's
   headings (Reports' days, the Field Manual's groups, alliance invites). */
.screen-play :is(.inv-section-title, .list-heading) {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--muted);
  font-size: 22px;
  text-transform: uppercase;
}

/* A section inside another (Troops' Infantry, Vehicles, Aircraft): a smaller title, set apart
   from what comes before it. */
.screen-play .inv-subsection-title {
  margin: 8px 0 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
  color: var(--muted);
  text-transform: uppercase;
}

/* Troop space (inventory_troops.js): its line over the inventory's bar, across the Troops section. */
.screen-play .troop-space {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.screen-play .inv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px;
}

.screen-play .inv-tile {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
  align-items: center;
  gap: 2px 8px;
  color: var(--gold-200);
}

.screen-play .inv-tile > .i {
  grid-row: 1 / span 2;
  width: 36px;
  height: 36px;
}

.screen-play .inv-tile-name {
  color: #d1dfeb;
  font-size: 14px;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.screen-play .inv-tile-amount {
  grid-column: 2;
  color: var(--text);
  font-weight: 600;
  font-size: 18px;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A good the bag has none of shows dimmed as a disabled troop tile does, its name alone beside
   its icon. A tile out of reach (a fixture above its building's level) dims the same. */
.screen-play .inv-tile.is-empty,
.screen-play .inv-tile.is-dim {
  opacity: var(--dim-opacity);
}

/* A tile that can go to work now (a spare fixture that fits its floor plan) is ringed green. */
.screen-play .inv-tile.is-ready {
  border-color: var(--ok);
  box-shadow: 0 0 0 2px var(--ok);
}

/* The Build & Install tile whose build is proposed on the floor plan is lit green, as its
   piece there is. */
.screen-play .inv-tile.is-proposed,
.screen-play .inv-tile.is-proposed:hover {
  border-color: var(--ok);
  background: var(--btn-go-fill);
}

/* A tile's own button (a fixture's Uninstall) spans the tile under its words. */
.screen-play .inv-tile > .btn {
  grid-column: 1 / -1;
}

.screen-play .inv-tile.is-empty > .inv-tile-name {
  grid-row: 1 / span 2;
}

.screen-play .inv-tile-flag {
  font-weight: 500;
  font-size: 12px;
  color: var(--gold-200);
}

.inv-bar {
  grid-column: 1 / -1;
  position: relative;
  height: 4px;
  margin-top: 2px;
  border-radius: 2px;
  background: #0b1519;
  box-shadow: inset 0 1px 1px #0008;
  overflow: hidden;
}

.inv-bar-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  background: linear-gradient(to bottom, #c9a35a, #8a7240);
}

.inv-bar-army {
  background: linear-gradient(to bottom, #e3bd6a, #a3803f);
}

.inv-bar-reserve {
  background: linear-gradient(to bottom, #6f8796, #465a66);
}

.screen-play .inv-tile-caption {
  grid-column: 1 / -1;
  color: var(--muted);
  font-size: var(--caption-text-size);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The Troops tab's Clankers box (clanker_box.js): buildings short of their crew, red with
   nobody yet and orange with some, then a small clanker per clanker under what it is doing. */
.screen-play .clk-short {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.screen-play .clk-short-row {
  padding: 4px 10px;
  border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
  border-radius: 6px;
  background: color-mix(in srgb, var(--danger) 12%, var(--surface-1));
  color: var(--danger);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.screen-play .clk-short-row.is-partial {
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 12%, var(--surface-1));
  color: var(--warn);
}

.screen-play .clk-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* A label and its count on the Troops tab: a clanker group's (Crafting 4) and troop space's. */
.screen-play :is(.clk-group-title, .troop-space-line) {
  display: flex;
  gap: 6px;
  color: #d1dfeb;
  font-size: 13px;
}

.screen-play :is(.clk-group-count, .troop-space-count) {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.screen-play .clk-icons {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
}

.screen-play .clk-icon {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--gold-200);
}

.screen-play .clk-icon > .i {
  width: 20px;
  height: 20px;
}

.screen-play button.clk-icon {
  cursor: pointer;
}

/* A clanker still walking over to its work shows faded. */
.screen-play .clk-icon.is-coming {
  opacity: 0.5;
}

.screen-play button.clk-icon:hover {
  color: var(--text);
}

/* The inventory's stock bars, and a battalion's troops (.troop-fill, on the army card and the
   battalion bar): a recessed rounded well with a hairline
   edge, and a fill lit from above. The gradient layers over each fill's own colour, so work and
   stalled segments keep theirs. */
.screen-play .inv-list .chip-well,
.screen-play .troop-fill.chip-well {
  height: 30px;
  border: 1px solid color-mix(in srgb, var(--gold-400) 22%, transparent);
  border-radius: 6px;
  background: linear-gradient(to bottom, #0f1a20, var(--chip-well));
  box-shadow: inset 0 1px 3px #0009;
  overflow: hidden;
}

.screen-play .inv-list .chip-fill,
.screen-play .troop-fill .chip-fill {
  background-image: linear-gradient(to bottom, #ffffff2e, #ffffff00 55%, #0000001f);
  box-shadow: inset -1px 0 0 color-mix(in srgb, var(--gold-200) 35%, transparent);
}

.screen-play .inv-list .chip-qty,
.screen-play .troop-fill .chip-qty {
  height: 30px;
  padding-left: 10px;
  text-shadow: 0 1px 2px #000c;
}

/* Text over a green fill reads in --ok-ink. A well whose fill is green says so with .fill-ok,
   set by its owner. The label spans the whole well, so its ink splits at the fill's edge (--fill,
   set by fillWell): dark over the green, the light ink past it over the empty track. A fill of any
   other colour keeps the light ink and its shadow. */
.screen-play .chip-well.fill-ok .chip-qty {
  color: transparent;
  text-shadow: none;
  background: linear-gradient(to right, var(--ok-ink) var(--fill), var(--text) var(--fill));
  -webkit-background-clip: text;
  background-clip: text;
}

.family-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.aspect-portrait .royal-slot {
  width: min(180px, 28vw);
  height: calc(min(180px, 28vw) * 260 / 180);
}

.morale-scale {
  margin: 0 0 12px;
}

.morale-scale-track {
  position: relative;
  display: flex;
  gap: 2px;
  min-height: 47px;
  padding-top: 8px;
  overflow: visible;
}

.morale-seg {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px 2px;
  font-size: 18px;
  line-height: 1;
  background: var(--chip-well);
  color: var(--ink);
  border: 1px solid var(--line);
  overflow: visible;
}

.morale-seg.is-current {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.morale-caret {
  position: absolute;
  top: -6px;
  z-index: 1;
  width: 0;
  height: 0;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-top: 12px solid var(--ink);
  transform: translateX(-50%);
  pointer-events: none;
}

.armies-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.army-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.army-card.is-leaderless {
  border-color: var(--danger);
}

/* An army place not yet taken: a card to start one from. */
.army-card.is-empty {
  border-style: dashed;
}

/* Under the card's portraits: its info, framed (arrows that move it in the
   list, its commander's name and Leadership, where it is, the restock
   switch), and beside it what can be done about it there; under them a
   march's bar. Disband sits on the card's top right corner. */
.army-foot {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

.army-info {
  flex: 1;
  min-width: 0;
  display: grid;
  align-content: center;
  gap: 2px;
  padding: 6px 8px;
  border: 1px solid var(--play-line);
  border-radius: 8px;
}

.army-head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.screen-play .icon-btn.army-move {
  flex: none;
  font-size: 11px;
}

/* The buttons stand as tall as the info beside them. */
.army-cta {
  flex: none;
  display: flex;
  align-items: stretch;
  gap: 6px;
}

.army-march .chip-fill {
  background-color: var(--ok);
}

.army-disband {
  position: absolute;
  top: calc(var(--icon-control-size-half) / -4);
  right: calc(var(--icon-control-size-half) / -4);
}

.army-where {
  margin: 0;
  color: var(--muted);
  font-size: var(--caption-text-size);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.screen-play button.btn.compact.army-go {
  flex: none;
  min-width: var(--icon-control-size);
  min-height: var(--icon-control-size);
  padding: 0 8px;
}

.screen-play button.btn.compact.army-deploy,
.screen-play button.btn.compact.army-send,
.army-standing {
  flex: none;
}

.army-standing {
  align-self: center;
  color: var(--muted);
  font-size: var(--caption-text-size);
  font-weight: 600;
}

.summon-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.summon-head {
  margin: 0;
  font-weight: 600;
}

.summon-army {
  display: flex;
  align-items: center;
  gap: 10px;
}

.summon-army.is-away {
  opacity: 0.45;
}

.summon-army .tray-pic {
  flex: none;
}

.summon-army-copy {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.summon-army-units {
  font-size: 13px;
  overflow-wrap: anywhere;
}

/* The battle dock: battle chrome along the bottom of the field, the pick's
   order line (battle/order_line.js) and, while an army is being placed,
   the deploy line (battle/deploy.js). Taps pass through it to the field
   except on the deploy line. */
.battle-dock {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 12px;
  z-index: var(--z-chrome);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.screen-play.map-open .battle-dock {
  display: none;
}

/* The pick's order: a status line. */
.battle-order {
  max-width: 100%;
  margin: 0;
  padding: 6px 12px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-panel);
  box-shadow: var(--shadow-sheet);
  color: var(--text);
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 15px;
  line-height: 1.2;
  text-align: center;
}

/* The deploy line (battle/deploy.js): while an army is being placed, what
   to tap and Cancel. */
.deploy-pick {
  order: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 100%;
  padding: 6px 8px 6px 14px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-panel);
  box-shadow: var(--shadow-sheet);
  color: var(--text);
  font-family: var(--font-ui);
  pointer-events: auto;
}

.deploy-pick[hidden] {
  display: none;
}

.deploy-pick-hint {
  margin: 0;
  font-weight: 600;
  font-size: 16px;
}

/* The attack line (attack_line.js): down the left edge of the raiders'
   view and their battle, floating boxes as the raider roster's on the
   right: the Auto deploy switch, then each army sent at them, its
   commander, troops by type and state, a march's bar, Deploy, and a
   corner button that sends it home; then Reinforce. The army on the field
   is lit gold. */
.attack-line {
  position: absolute;
  top: 8px;
  left: 16px;
  z-index: var(--z-chrome);
  display: grid;
  gap: 4px;
  width: min(168px, calc(50% - 24px));
  max-height: calc(100% - 96px);
  padding: 6px 6px 0 0;
  color: var(--text);
  font-family: var(--font-ui);
  overflow-y: auto;
}

.attack-line-auto,
.attack-line-army {
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-hud);
  box-shadow: var(--shadow-sheet);
  backdrop-filter: var(--hud-blur);
}

.attack-line[hidden],
.screen-play.map-open .attack-line {
  display: none;
}

/* The line holds the top-left corner on raiders: the account and morale
   chips that hang there step aside, and so does the day clock
   (visit_bar.js visiting-raiders). */
.screen-play.visiting-raiders .play-left-cluster,
.screen-play.visiting-raiders .day-clock {
  display: none;
}

/* On raiders the settings button joins the top row right of the banner at
   every width, as the wide band has it, so the roster under the row keeps
   the right edge to itself. */
.screen-play.visiting-raiders .play-top-controls {
  position: relative;
  inset: auto;
  flex: none;
  align-items: center;
  margin: var(--play-top-gap) var(--play-top-inset) 0 0;
}

.screen-play.visiting-raiders .top-bar {
  margin-right: 0;
}

.attack-line-auto {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 8px;
  font-weight: 600;
  font-size: var(--caption-text-size);
  cursor: pointer;
}

.attack-line-army {
  position: relative;
  display: grid;
  gap: 4px;
  padding: 4px;
}

.attack-line-reject {
  position: absolute;
  top: calc(var(--icon-control-size-half) / -4);
  right: calc(var(--icon-control-size-half) / -4);
}

.attack-line-army.is-field {
  border-color: var(--gold-400);
  box-shadow: 0 0 0 2px var(--gold-400);
}

.attack-line-army.is-fallen {
  filter: grayscale(1);
}

.attack-line-entry {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.attack-line-face {
  position: relative;
  flex: none;
  width: 32px;
  height: 32px;
  overflow: hidden;
  border-radius: var(--card-pic-radius);
  background: var(--portrait-ground);
}

.attack-line-copy {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.attack-line-name {
  overflow: hidden;
  font-weight: 600;
  font-size: var(--caption-text-size);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.attack-line-troops {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 4px;
  font-size: var(--caption-text-size);
  font-variant-numeric: tabular-nums;
}

.attack-line-troop {
  width: 16px;
  height: 16px;
  image-rendering: pixelated;
}

.attack-line-state {
  color: var(--muted);
  font-size: var(--caption-text-size);
  font-variant-numeric: tabular-nums;
}

.attack-line-march .chip-fill {
  background-color: var(--ok);
}

.screen-play .attack-line .troop-fill.chip-well,
.screen-play .attack-line .troop-fill .chip-qty {
  height: 18px;
  font-size: var(--caption-text-size);
}

/* On a phone the line narrows beside the roster. */
.aspect-portrait .attack-line {
  width: 136px;
}

/* The raider roster (raider_roster.js): down the right edge of the raiders'
   view and their battle, a box per group they stand with: its picture,
   and beside it the group's name over its bar. A tap on a box looks at
   its group. Once a battle is on view it narrows to pictures over bars,
   the names fading out. A gone group's box
   slides off to the right, then the boxes under it close up; coming back,
   the space opens, then the box slides in. The box is see-through while
   its fold is shut, so nothing clips it, picture and shadow included. */
.raider-roster {
  position: absolute;
  top: 8px;
  right: 16px;
  z-index: var(--z-chrome);
  display: flex;
  flex-direction: column;
  --roster-width: min(132px, calc(50% - 24px));
  width: var(--roster-width);
  pointer-events: none;
  transition: width calc(var(--panel-leave-ms) * 1ms) var(--panel-leave-ease);
}


.raider-roster[hidden],
.screen-play.map-open .raider-roster {
  display: none;
}

.roster-entry {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows calc(var(--panel-leave-ms) * 1ms) var(--panel-leave-ease);
}

.roster-entry.is-gone {
  grid-template-rows: 0fr;
  transition-delay: calc(var(--panel-leave-ms) * 1ms);
}

.roster-fold {
  min-height: 0;
}

/* The picture pokes out of the box's top left corner, the name beside it
   and the bar under both. The top margin keeps the picture off the box
   above. */
.roster-card {
  position: relative;
  width: 100%;
  margin-top: 8px;
  display: grid;
  gap: 3px;
  padding: 4px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-hud);
  box-shadow: var(--shadow-sheet);
  backdrop-filter: var(--hud-blur);
  color: var(--text);
  font-family: var(--font-ui);
  font-variant-numeric: tabular-nums;
  text-align: left;
  pointer-events: auto;
  transition:
    transform calc(var(--panel-leave-ms) * 1ms) var(--panel-leave-ease) calc(var(--panel-leave-ms) * 1ms),
    opacity calc(var(--panel-leave-ms) * 1ms) var(--panel-leave-ease) calc(var(--panel-leave-ms) * 1ms);
}

.roster-entry.is-gone .roster-card {
  transform: translateX(calc(100% + 16px));
  opacity: 0;
  pointer-events: none;
  transition-delay: 0s;
}

.roster-pic {
  position: absolute;
  top: -8px;
  left: -8px;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: var(--panel-border);
  border-radius: var(--card-pic-radius);
  background: var(--portrait-ground);
  color: var(--alert);
}

.roster-pic .i {
  width: 16px;
  height: 16px;
}

.roster-face {
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}

/* A building group's level, a badge on its picture's bottom right. */
.roster-level {
  position: absolute;
  right: -6px;
  bottom: -3px;
  min-width: 13px;
  padding: 0 3px;
  border-radius: 7px;
  background: var(--gold-400);
  color: var(--on-accent);
  font-size: 9px;
  font-weight: 700;
  line-height: 13px;
  text-align: center;
}

.roster-title {
  padding-left: 22px;
  transition: opacity calc(var(--panel-leave-ms) * 1ms) var(--panel-leave-ease);
  overflow: hidden;
  font-weight: 600;
  font-size: var(--caption-text-size);
  line-height: 1.2;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.screen-play .raider-roster .troop-fill.chip-well,
.screen-play .raider-roster .troop-fill .chip-qty {
  height: 16px;
  font-size: var(--caption-text-size);
}

/* On a phone the roster narrows beside the attack line. */
.aspect-portrait .raider-roster {
  --roster-width: 112px;
}

/* A battle narrows it on any screen, after the phone's width so it wins,
   and keeps each name's room so the bars stay put as the names fade. */
.raider-roster.is-battle {
  --roster-width: 56px;
}

.raider-roster.is-battle .roster-title {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .raider-roster,
  .roster-title,
  .roster-entry,
  .roster-card {
    transition-duration: 0s;
    transition-delay: 0s;
  }
}

/* The replay bar (battle/replay_bar.js): while a replay plays, Play or
   Pause, the rate, the scrubber under its strip of moment marks, where it
   is of how long, and Copy link; over them at the last tick, how the
   battle ended and Watch again. At phone width the scrubber takes what
   the buttons leave. */
.replay-bar {
  order: 2;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: min(560px, 100%);
  padding: 8px 10px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-panel);
  box-shadow: var(--shadow-sheet);
  color: var(--text);
  font-family: var(--font-ui);
  pointer-events: auto;
}

.replay-end,
.replay-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.replay-end[hidden] {
  display: none;
}

.replay-result {
  flex: 1;
  margin: 0;
  font-weight: 600;
  font-size: 15px;
}

.screen-play .replay-controls .btn.compact {
  min-width: 40px;
  min-height: 40px;
  padding: 0 8px;
  display: inline-grid;
  place-items: center;
}

.replay-controls .i {
  width: 18px;
  height: 18px;
}

.replay-track {
  --replay-scrub-h: 20px;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Over the marks' lines, so its thumb and track draw on top of them. */
.replay-scrub {
  position: relative;
  z-index: 1;
  width: 100%;
  height: var(--replay-scrub-h);
  margin: 0;
  accent-color: var(--accent);
}

/* The strip's ends sit over the scrubber thumb's centre at 0 and at the
   end. A tap anywhere on it takes the nearest mark (MARK_HIT_PX). */
.replay-marks {
  --replay-thumb-half: 8px;
  position: relative;
  height: 18px;
  margin: 0 var(--replay-thumb-half);
  cursor: pointer;
}

/* A mark is its moment's place on the strip, in its colour: a line from
   its dot down across the scrubber's track, and the dot over it. Neither
   takes a tap or a drag; the strip and the scrubber do. */
.replay-mark {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 0;
  pointer-events: none;
}

.replay-mark.side-0,
.replay-mark.fall {
  color: var(--replay-side-0);
}

.replay-mark.side-1 {
  color: var(--replay-side-1);
}

.replay-tick {
  position: absolute;
  top: 50%;
  bottom: calc(var(--replay-scrub-h) / -2 - 4px);
  left: 0;
  width: 2px;
  transform: translateX(-50%);
  background: currentColor;
  opacity: 0.75;
}

.replay-dot {
  position: absolute;
  top: 50%;
  left: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: currentColor;
  box-shadow: 0 0 0 1.5px var(--chrome);
}

/* A building falling: a bigger diamond, over the deploys it crowds. */
.replay-mark.fall {
  z-index: 1;
}

.replay-mark.fall .replay-dot {
  width: 11px;
  height: 11px;
  border-radius: 2px;
  transform: translate(-50%, -50%) rotate(45deg);
  box-shadow: 0 0 0 1.5px var(--chrome), 0 0 6px color-mix(in srgb, currentColor 70%, transparent);
}

.replay-time {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The battalion bar (battle/battalion_bar.js): a row of small tiles per
   army of the viewer's on the field, along the bottom of the dock over the
   menu bar, on a see-through box. The commander's bust, then each
   battalion's portrait with its count across its foot. A selected
   battalion is outlined in gold with its toggle over it; one holding is
   bordered red under a Held badge; a fallen one is grey. The commander's
   toggle and Retreat! open over the bust. Tiles narrow to keep a row on
   screen, and the toggles stand in the room above the row. */
.battalion-bar {
  --bar-tile: 48px;
  order: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 36px;
  max-width: 100%;
  padding-top: 32px;
}

.bar-army {
  display: flex;
  gap: 6px;
  max-width: 100%;
  padding: 6px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-hud);
  box-shadow: var(--shadow-sheet);
  backdrop-filter: var(--hud-blur);
  color: var(--text);
  font-family: var(--font-ui);
  pointer-events: auto;
}

.bar-commander,
.bar-battalion {
  position: relative;
  flex: 0 1 auto;
  width: var(--bar-tile);
  min-width: 0;
}

.bar-battalions {
  display: flex;
  flex: 0 1 auto;
  gap: 6px;
  min-width: 0;
}

/* A toggle stands centred over its tile; the commander's pair starts at
   the bust. */
.bar-over {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  translate: -50% 0;
}

.bar-actions {
  left: 0;
  translate: none;
  display: flex;
  gap: 4px;
}

.screen-play .battalion-bar .btn.compact {
  width: max-content;
  min-width: var(--bar-tile);
  padding: 0 8px;
  font-size: 12px;
}

.bar-hold.is-gone,
.bar-actions.is-gone {
  visibility: hidden;
}

.bar-face,
.bar-pic {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  padding: 0;
  border: 2px solid var(--play-line);
  border-radius: 8px;
  cursor: pointer;
}

/* The commander's bust fills its square on the Settlers panel's ground; a
   tap looks at them. */
.bar-face {
  position: relative;
  overflow: hidden;
  background: var(--portrait-ground);
}

.bar-troop {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 6px;
  image-rendering: pixelated;
}

.bar-pic {
  background: var(--surface-2);
  line-height: 0;
}

.bar-pic:hover:not(:disabled) {
  border-color: var(--play-line-strong);
}

.bar-battalion.is-held .bar-pic {
  border-color: var(--danger);
}

.bar-pic.is-selected,
.bar-pic.is-selected:hover {
  border-color: var(--gold-400);
  box-shadow: 0 0 0 2px var(--gold-400);
}

.bar-pic:disabled {
  cursor: default;
}

.bar-battalion .troop-fill {
  position: absolute;
  right: 4px;
  bottom: 4px;
  left: 4px;
  pointer-events: none;
}

.bar-held {
  position: absolute;
  top: -6px;
  left: 50%;
  translate: -50% 0;
  padding: 0 4px;
  border-radius: 6px;
  background: var(--danger);
  color: var(--on-accent);
  font-size: 9px;
  font-weight: 700;
  line-height: 12px;
  pointer-events: none;
  display: none;
}

.bar-battalion.is-held .bar-held {
  display: block;
}

.bar-battalion.is-fallen {
  filter: grayscale(1);
  opacity: 0.45;
}

/* The combat bar (combat_bar.js): in the resource bar's place while a
   battle is on screen, who fights whom and how long it has run, over when
   the attack ends on the clock and the waves still to come; on raiders on
   view with no battle, scouting them. A battle also hides Edit and Build
   and morale. */
.screen-play.in-combat .city-play-btns,
.screen-play.in-combat .morale-chip,
.screen-play.in-combat .morale-progress {
  display: none;
}

.screen-play.city-visiting.in-combat .top-bar:not([hidden]),
.screen-play.city-visiting.scouting .top-bar:not([hidden]) {
  display: flex;
}

/* While the top bar carries the combat bar (a battle, or scouting raiders)
   it reads as a banner: red-edged, glowing, its title in display caps
   behind crossed swords. */
.screen-play:is(.in-combat, .scouting) .top-bar {
  border-color: var(--alert);
  background: linear-gradient(115deg, color-mix(in srgb, var(--btn-negative) 45%, var(--surface-2)), var(--surface-1) 70%);
  box-shadow: var(--shadow-sheet), 0 0 18px color-mix(in srgb, var(--alert) 35%, transparent);
}

.combat-bar {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2px;
}

.combat-bar[hidden] {
  display: none;
}

.combat-bar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
  font-weight: 600;
}

.combat-bar-head > .i {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--alert);
}

.combat-bar-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--selected-ink);
  font-family: var(--font-display);
  font-size: var(--card-title-size);
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.1;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.combat-bar-clock {
  flex: none;
  font-family: var(--font-display);
  font-size: var(--card-title-size);
  font-variant-numeric: tabular-nums;
}

/* On a phone the banner steps down a size and drops its icon, so the kind
   and level fit beside the way back and the settings button. */
.aspect-portrait :is(.combat-bar-title, .combat-bar-clock) {
  font-size: 18px;
  letter-spacing: 0.02em;
}

.aspect-portrait .combat-bar-head > .i {
  display: none;
}

.combat-bar-waves {
  display: grid;
  margin: 0;
  color: var(--muted);
  font-size: var(--meta-text-size);
}

/* The clock's fill drains behind its line, a second at a time, and turns red near the end. */
.combat-bar-ends {
  position: relative;
  overflow: hidden;
  margin: 0;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--surface-2);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.combat-bar-ends-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: color-mix(in srgb, var(--info-deep) 55%, transparent);
  transition: width 1s linear;
}

.combat-bar-ends-urgent .combat-bar-ends-fill {
  background: color-mix(in srgb, var(--alert) 60%, transparent);
}

.combat-bar-ends-text {
  position: relative;
}

.combat-bar-ends[hidden],
.combat-bar-waves[hidden] {
  display: none;
}

/* Along the card's top: its commander's portrait, a little wider than a
   battalion's, then a slot per battalion position, kept compact on a wide
   panel. */
.army-roster {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) repeat(5, minmax(0, 1fr));
  gap: var(--army-slot-gap);
  align-items: stretch;
  max-width: 440px;
}

/* The commander's portrait, which picks or changes who leads, as tall as a
   slot with its bar. */
.army-commander,
.army-slot-pic {
  display: grid;
  place-items: center;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--play-line);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--muted);
}

.army-commander-face,
.army-troop-face {
  width: 100%;
  height: 100%;
}

.army-commander-face {
  object-fit: cover;
}

.army-slot-pic {
  width: 100%;
  aspect-ratio: 1;
}

button.army-commander:hover,
button.army-slot-pic:hover {
  border-color: var(--play-line-strong);
  background: var(--surface-3);
}

.army-commander-face {
  background: #f6f1e5;
}

.army-troop-face {
  image-rendering: pixelated;
}

.army-plus {
  font-size: 28px;
  line-height: 1;
}

.army-commander-name {
  min-width: 0;
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.army-commander-lead {
  flex: none;
  color: var(--muted);
  font-size: var(--caption-text-size);
  white-space: nowrap;
}

/* A slot is its troop's portrait or an unused frame, a half close button
   on its corner that clears it, and under it its troops against its target
   as a bar as wide. */
.army-slot {
  display: grid;
  align-content: start;
  gap: var(--army-slot-gap);
  min-width: 0;
}

/* A position with no battalion reads unused: a darker, dashed square over
   an empty bar. A battalion with no troops left dims. */
.army-slot.is-unused .army-slot-pic {
  border-style: dashed;
  background: var(--surface-1);
}

.army-slot.is-out .army-slot-pic {
  opacity: var(--dim-opacity);
}

.army-slot-face {
  position: relative;
}

/* A half close button on the slot face's top right corner, mostly inside it. */
.army-slot-clear {
  position: absolute;
  top: calc(var(--icon-control-size-half) / -4);
  right: calc(var(--icon-control-size-half) / -4);
}

.screen-play .troop-fill.chip-well {
  height: var(--troop-fill-height);
}

.screen-play .troop-fill .chip-qty {
  justify-content: center;
  height: var(--troop-fill-height);
  padding: 0;
  font-size: 12px;
}

.screen-play .battalion-bar .troop-fill.chip-well,
.screen-play .battalion-bar .troop-fill .chip-qty {
  height: 12px;
  font-size: 9px;
}

/* An army card's battalion at its target fills green, one short amber, and
   one with none red right across. */
.troop-fill.fill-ok .chip-fill {
  background-color: var(--ok);
}

.troop-fill.is-short .chip-fill {
  background-color: var(--warn);
}

.troop-fill.is-out .chip-fill {
  background-color: var(--danger);
}

/* The slot the troop picker is filling. */
.army-slot-pic.is-picking,
.army-slot-pic.is-picking:hover {
  border-color: var(--gold-400);
  box-shadow: 0 0 0 2px var(--gold-400);
}

/* The troop picker's portraits are square, as the slots are. */
.tray-face.is-troop .tray-pic {
  border: 1px solid var(--play-line);
  border-radius: 8px;
  background: var(--surface-2);
}

/* The troop type the slot holds now, lit as the slot is. */
.tray-face.is-troop.is-current,
button.tray-face.is-troop.is-current:hover {
  border-color: var(--gold-400);
  box-shadow: 0 0 0 2px var(--gold-400);
}

/* What the slot would hold of a type against how many wait in reserve:
   green with enough, gold when the reserve falls short, red with none. */
.army-supply {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.army-supply.is-enough {
  color: var(--ok);
}

.army-supply.is-short {
  color: var(--gold-400);
}

.army-supply.is-none {
  color: var(--danger);
}

.army-restock {
  flex-direction: row;
  align-items: center;
  justify-self: start;
  gap: 8px;
  cursor: pointer;
}

/* A switch that waits (a blank card, an army out on the map) reads as off-limits. */
.army-restock:has(input:disabled) {
  opacity: var(--dim-opacity);
  cursor: not-allowed;
}

.member-leads {
  color: var(--muted);
  font-size: var(--caption-text-size);
}

.morale-impacts-title {
  margin: 8px 0 0;
  font-size: 1rem;
  font-weight: 600;
}

.morale-impacts {
  margin: 4px 0 8px;
  padding-left: 1.2em;
}

.morale-cols {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.morale-col {
  flex: 1 1 0;
  min-width: 0;
}

.morale-col h3,
.morale-improve h3 {
  margin: 0 0 6px;
  font-size: 0.85rem;
  font-weight: 600;
}

.morale-col-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.morale-improve {
  margin-top: 12px;
}

/* The game skin is shared by play, the title menu, and the family designer. */
:is(.screen-play, .screen-title, .screen-designer) {
  /* The developer's and the System's colours: their chat lines, and the tracker's developer
     section and log. */
  --developer: #7fd08c;
  --system: var(--selected-ink);
  /* Non-portrait caps the resource row and the chip row here and centers them.
     The clock query below matches this same 720px with a literal. */
  --play-top-band: 720px;
  --play-top-gap: 8px;
  /* The clock holds this width so the account and morale row can stop short of it. */
  --corner-clock-width: 160px;
  --corner-clock-reserve: 0px;
  --panel-fixed-max: 745px;
  --panel-inset: 14px;
  --panel-border-width: 1px;
  --panel-border: var(--panel-border-width) solid var(--panel-line);
  --control-height: 32px;
  --compact-control-height: 24px;
  --icon-control-size: 30px;
  --icon-control-size-half: calc(var(--icon-control-size) / 2);
  --panel-tab-selected-height: 40px;
  /* The gap between an army card's portraits and under a battalion's, and
     its troop bar. */
  --army-slot-gap: 6px;
  --troop-fill-height: 22px;
  /* A term pill's height and its icon box's width (build_tile.js). */
  --term-height: 40px;
  --control-text-size: 14px;
  --panel-title-size: 24px;
  --meta-text-size: 14px;
  --caption-text-size: 12px;
  --card-title-size: 22px;
  /* A tile's or call box's name: a build tile, a radio contact. */
  --tile-title-size: 18px;
  /* Recessed fields use the panel and raised surfaces. */
  --surface-well: linear-gradient(var(--surface-1), var(--surface-2));
  --well-line: #6c818866;
  --gold-500: #d4a44a;
  --gold-700: #c49238;
  --olive: #3c3624;
  --olive-line: #c6a45a;
  --info: #78d5fa;
  --info-deep: #4a92dc;
  --alert: #ff858c;
  --alert-ink: #1a0a0c;
  --ok: #3dbe78;
  --ok-bg: #173d2c;
  --ok-text: #b2f3d5;
  --ok-ink: #06180f;
  --font-body: "Source Sans 3", "Segoe UI", system-ui, sans-serif;
  --font-ui: "Barlow Semi Condensed", "Segoe UI", system-ui, sans-serif;
  --font-pixel: "Press Start 2P", monospace;
  --bg: var(--surface-0);
  --ink: var(--text);
  --line: var(--play-line);
  --line-faint: color-mix(in srgb, var(--line) 25%, transparent);
  --accent: var(--gold-400);
  --on-accent: var(--gold-ink);
  --danger: var(--alert);
  --chip-well: #1b2b34;
  --chip-fill: #71603c;
  --chip-fill-work: #94804b;
  --btn-positive-ink: var(--gold-ink);
  --btn-positive-fill: #ffdf813d;
  --btn-go: var(--ok);
  --btn-go-ink: var(--ok-ink);
  --btn-go-fill: #8ee8b63d;
  --btn-start: var(--info);
  --btn-start-ink: #04141c;
  --btn-start-fill: #b6e9ff3d;
  --btn-negative: #b84d56;
  --btn-negative-fill: #ff858c33;
  --btn-neutral: var(--surface-2);
  --btn-neutral-ink: var(--text);
  --btn-neutral-fill: #bad8ef20;
  --scroll: #b5ccd738;
  --scroll-hover: #b5ccd760;
  --row-selected: #f4c75f18;
  --meter-fill: linear-gradient(#ffe998, #f5bc43 70%, #efb640);
  --ms-turn: 1400ms;
  --chat-dock-tablet: min(380px, 38vw);
  --chat-list: 120px;
  --chat-thread: clamp(280px, 25vw, 380px);
  --chat-dock-landscape: calc(var(--chat-list) + var(--chat-thread));
  color-scheme: dark;
  color: var(--text);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 14px;
  --skin-panel: #26221d;
  --skin-raised: #3d352b;
  --skin-line: #b5a38d;
  --skin-text: #f8eedf;
  --skin-muted: #d8c6af;
  --skin-accent: #f4bc64;
  --skin-selected: #5a4328;
  --font-display: "Barlow Condensed", sans-serif;
  --play-width: min(var(--float-width), calc(100% - 24px));
  --play-top-inset: 0px;
  --play-control-inset: 0px;
  --panel-radius: 0px;
  --control-radius: 0px;
  --surface-panel: var(--skin-panel);
  --surface-hud: var(--skin-panel);
  --panel: var(--skin-panel);
  --surface-1: var(--skin-panel);
  --surface-2: var(--skin-raised);
  --play-line: var(--skin-line);
  --play-line-strong: var(--skin-line);
  --text: var(--skin-text);
  --muted: var(--skin-muted);
  --gold-200: var(--skin-accent);
  --gold-400: var(--skin-accent);
  --btn-neutral-face: var(--skin-raised);
  --btn-negative-face: var(--alert);
  --btn-negative-ink: var(--skin-panel);
  --btn-negative-line: var(--alert);
  --selected-fill: var(--skin-selected);
  --selected-ink: var(--skin-text);
  --selected-line: var(--skin-accent);
  --selected-glow: none;
  --shadow-card: none;
  --shadow-sheet: none;
  --btn-shadow: none;
  --shadow: none;
  --panel-line: var(--skin-line);
  --surface-0: var(--skin-panel);
  --surface-3: var(--skin-raised);
  --text-dim: var(--skin-muted);
  --chrome: var(--skin-panel);
  --hud-blur: none;
  --panel-radius-inner: 0px;
  --btn-positive: var(--skin-accent);
  --gold-ink: var(--skin-panel);
  --chat-room-line: var(--skin-raised);
}

.aspect-portrait .screen-play {
  --corner-clock-width: 136px;
  --menu-inset: 6px;
  --meta-text-size: 13px;
  --caption-text-size: 11px;
}

.screen-play .i {
  width: 22px;
  height: 22px;
  display: block;
  flex: none;
  text-shadow: none;
  filter: none;
}

:is(.screen-play, .screen-title, .screen-designer) :is(button, input):focus-visible {
  outline: 2px solid var(--skin-accent);
  outline-offset: -3px;
}

:is(.screen-play, .screen-title, .screen-designer) .btn,
.screen-play .trait,
.screen-play .dest-item,
.screen-play .tool,
.screen-play .res,
.screen-play .clock,
.screen-play .pill,
.screen-play .member-meta,
.screen-play .xp-line,
.screen-play .card-meta,
.screen-play .card-line,
.screen-play .card-status,
.screen-play .menu-bar button {
  font-family: var(--font-ui);
}

:is(.screen-play, .screen-title, .screen-designer) button.btn {
  gap: 6px;
  min-height: var(--control-height);
  padding: 0 10px;
  border: 1px solid var(--play-line-strong);
  border-radius: var(--control-radius);
  font-weight: 600;
  font-size: var(--control-text-size);
  line-height: 1;
  white-space: nowrap;
  box-shadow: var(--btn-shadow);
}

:is(.screen-play, .screen-title, .screen-designer) button.btn.positive {
  background: var(--skin-accent);
  border-color: var(--skin-accent);
  color: var(--skin-panel);
  text-shadow: none;
}

:is(.screen-play, .screen-title, .screen-designer) button.btn.go {
  background-color: var(--ok);
  background-image: linear-gradient(#6fdc9f, #2f9e62);
  color: var(--btn-go-ink);
  border-color: #8ee8b6;
}

.screen-play button.btn.start {
  background-color: var(--info);
  background-image: linear-gradient(#9fe2ff, #3c9fd0);
  color: var(--btn-start-ink);
  border-color: #b6e9ff;
}

:is(.screen-play, .screen-title, .screen-designer) button.btn.neutral {
  background: var(--btn-neutral-face);
  border-color: var(--play-line-strong);
  color: var(--text);
  text-shadow: none;
}

:is(.screen-play, .screen-title, .screen-designer) button.btn.discord {
  background: var(--btn-discord);
  border-color: var(--btn-discord);
  color: var(--btn-discord-ink);
  text-shadow: none;
}

.screen-play button.btn.negative {
  background: var(--btn-negative-face);
  color: var(--alert-ink);
  border-color: var(--btn-negative-line);
}

:is(.screen-play, .screen-title, .screen-designer) button.btn:disabled,
:is(.screen-play, .screen-title, .screen-designer) button.btn[aria-disabled="true"] {
  opacity: 1;
  filter: saturate(0.35) brightness(0.85);
}

.screen-play button.btn.compact {
  min-height: var(--compact-control-height);
  padding: 0 10px;
  font-size: 13px;
}

.screen-play .icon-btn {
  width: var(--icon-control-size);
  height: var(--icon-control-size);
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: var(--control-radius);
  border: 1px solid var(--play-line-strong);
  box-shadow: none;
  background: var(--btn-neutral-face);
  color: var(--text);
  text-shadow: none;
}

.screen-play .icon-btn .i {
  width: 20px;
  height: 20px;
}

/* The close button (button.js closeFace, bindClose): every dismiss and
   remove in play, at the icon control size or, with .half, half of it. The
   icon fills two thirds of a full face; on a half face it fills the face, so
   the X stays legible. */
.screen-play .close-btn {
  --close-size: var(--icon-control-size);
  --close-icon: calc(var(--close-size) * 2 / 3);
  flex: none;
  width: var(--close-size);
  height: var(--close-size);
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--play-line-strong);
  border-radius: var(--control-radius);
  box-shadow: var(--btn-shadow);
  background: var(--btn-neutral-face);
  color: var(--text);
  text-shadow: none;
}

.screen-play .close-btn.half {
  --close-size: var(--icon-control-size-half);
  --close-icon: 100%;
}

.screen-play .close-btn .i {
  width: var(--close-icon);
  height: var(--close-icon);
  filter: none;
}

/* An armed close (button.js bindConfirmClose) trades its X for words and widens to fit them. */
.screen-play .close-btn.armed {
  width: auto;
  padding: 0 12px;
}

.close-btn-words {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: var(--control-text-size);
  white-space: nowrap;
}

/* The Back (button.js bindBack): an amber button with a left chevron, as
   tall as the close it sits beside. */
.screen-play button.btn.back-btn {
  min-height: var(--icon-control-size);
  padding: 0 12px 0 6px;
  gap: 2px;
  background: var(--skin-accent);
  border-color: var(--skin-accent);
  color: var(--skin-panel);
  text-shadow: none;
}

.screen-play .back-btn .i {
  width: 18px;
  height: 18px;
}

.screen-play .hud-bar,
.screen-play .top-bar {
  padding: 6px 10px;
  background: var(--surface-panel);
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  box-shadow: inset 0 1px 2px #ffffff18, 0 2px 4px #0004;
  color: var(--text);
}

.screen-play .top-bar {
  flex: 1;
  margin: var(--play-top-gap) var(--play-top-inset) 0;
  box-shadow: none;
  background: var(--skin-panel);
  border: 0;
  border-left: 8px solid var(--skin-accent);
  padding: 10px 14px;
}

/* 720px matches --play-top-band. A container query does not resolve var().
   Under that width the clock stays in the corner. The row's own width is the measure, so an
   open radio dock can drop the clock back out. The clock fills the row so it reads as one band
   with the resource bar. Only the bar and the clock set the row's height: the tools stack and
   the debug stack are overlays, so an open debug panel or the dev debug button cannot stretch
   the bar. */
@container play-top style(--top-band: yes) and (min-width: 720px) {
  .play-top-row > .top-bar {
    margin: var(--play-top-gap) 0 0 var(--play-top-inset);
  }

  /* The band joins the clock and tools to the resource bar. Before the first resource the bar
     is hidden and they stay in the corner, beside the account and morale row. */
  /* The column is the clock's height, so the tools hang the row's gap under the clock. */
  .play-top-row:has(> .top-bar:not([hidden])) > .play-top-controls {
    position: relative;
    inset: auto;
    flex: 0 0 auto;
    align-self: flex-start;
    align-items: stretch;
    margin: var(--play-top-gap) var(--play-top-inset) 0 0;
    padding-right: 0;
  }

  .play-top-row:has(> .top-bar:not([hidden])) > .play-top-hud .play-left-cluster {
    margin-right: 0;
  }

  .play-top-row:has(> .top-bar:not([hidden])) > .play-top-controls > .hud-tools {
    position: absolute;
    top: calc(100% + var(--play-top-gap));
    right: 0;
  }

  .play-top-row:has(> .top-bar:not([hidden])) .play-right {
    position: relative;
    align-self: stretch;
    gap: 0;
  }

  .play-top-row:has(> .top-bar:not([hidden])) .play-right > .clock {
    flex: 1 1 auto;
  }
}

.screen-play .chips {
  justify-content: space-between;
  gap: 12px;
}

.screen-play .res {
  --res-icon-size: 24px;
  flex: 1;
  display: grid;
  grid-template-columns: var(--res-icon-size) minmax(0, 1fr);
  align-items: center;
  gap: 0 5px;
  min-width: 0;
  color: var(--gold-200);
}

.screen-play .res .i {
  grid-column: 1;
  grid-row: 1;
  width: var(--res-icon-size);
  height: var(--res-icon-size);
}

.screen-play .res-copy {
  display: contents;
}

.screen-play .res-name {
  grid-column: 2;
  grid-row: 1;
  color: var(--muted);
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: .12em;
}

.screen-play .res-line {
  grid-column: 1 / -1;
  grid-row: 2;
  display: flex;
  align-items: baseline;
  gap: 6px;
  color: var(--text);
  white-space: nowrap;
}

.screen-play .res-val {
  font-weight: 600;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  font-size: 25px;
}

.screen-play .res-flag {
  font-weight: 500;
  font-size: 12px;
  color: var(--gold-200);
}

.screen-play .res-flag[data-tone="alert"] {
  color: var(--alert);
}

/* Sky clock. The lens is the phase and the weather in one picture; the copy is hudClock().
   hud.js paints data-phase, data-weather, data-lamps, --solar, and is-turning for --ms-turn. */
.screen-play .clock {
  width: var(--corner-clock-width);
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 12px 0 0;
  --clock-line: var(--play-line-strong);
  color: var(--text);
  text-align: left;
  transition: background-color var(--ms-turn) ease, border-color var(--ms-turn) ease;
  box-shadow: none;
  background: var(--skin-panel);
  border: 0;
  --clock-radius: 0px;
  --clock-radius-inner: var(--clock-radius);
}

.screen-play:has(.day-clock:not([hidden])) {
  --corner-clock-reserve: calc(var(--corner-clock-width) + 8px);
}

.screen-play .clock[hidden] {
  display: none;
}

.screen-play .clock[data-phase="night"] {
  background-color: #141c30;
  --clock-line: #7b8fc599;
}

.screen-play .clock-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  padding: 5px 0;
}

.screen-play .clock.is-turning .clock-copy {
  animation: clock-copy-swap var(--ms-turn) ease;
}

.screen-play .clock-time {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 20px;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.screen-play .clock-sub {
  /* The width of "daybreak 18m 00s", its longest value, so the box holds still. */
  min-width: 14ch;
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--caption-text-size);
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--muted);
}

.screen-play .clock-sky {
  --lens: 48px;
  --r: calc(var(--lens) * 0.36);
  --ang: calc((var(--solar) - 0.25) * 1turn);
  position: relative;
  flex: none;
  align-self: stretch;
  width: var(--lens);
  min-height: var(--lens);
  border-radius: var(--clock-radius-inner) 0 0 var(--clock-radius-inner);
  overflow: hidden;
  background: #4aa3e6;
  background-clip: padding-box;
  isolation: isolate;
  transition: --solar 400ms linear;
  border-right: 0;
  box-shadow: none;
}

.screen-play .clock.is-turning .clock-sky {
  transition-duration: var(--ms-turn);
  transition-timing-function: cubic-bezier(0.5, 0, 0.3, 1);
}

.screen-play .clock-sky > :where(span) {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.screen-play .sky-day {
  background: linear-gradient(#3f9be3, #9fd3f2 58%, #e6cf8e);
}

.screen-play .sky-night {
  background: linear-gradient(#070c22, #16234b 60%, #2a3a66);
  opacity: 0;
  transition: opacity var(--ms-turn) ease;
}

.screen-play .clock[data-phase="night"] .sky-night {
  opacity: 1;
}

.screen-play .sky-stars {
  opacity: 0;
  transition: opacity calc(var(--ms-turn) * 0.6) ease calc(var(--ms-turn) * 0.4);
}

/* The stars twinkle on their own layer, by opacity alone, so the compositor
   runs it while .sky-stars fades in and out for night. */
.screen-play .sky-stars::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(1.4px 1.4px at 18% 22%, #fff 95%, transparent),
    radial-gradient(1px 1px at 38% 12%, #fff, transparent),
    radial-gradient(1.2px 1.2px at 62% 30%, #fff, transparent),
    radial-gradient(1px 1px at 80% 16%, #fff, transparent),
    radial-gradient(1.4px 1.4px at 72% 44%, #fff, transparent),
    radial-gradient(1px 1px at 28% 46%, #fff, transparent),
    radial-gradient(1px 1px at 50% 36%, #fffb, transparent),
    radial-gradient(1px 1px at 12% 40%, #fffb, transparent);
}

.screen-play .clock[data-phase="night"] .sky-stars {
  opacity: 1;
}

.screen-play .clock[data-phase="night"] .sky-stars::before {
  animation: clock-twinkle 3.2s ease-in-out infinite alternate;
}

.screen-play .sky-sun,
.screen-play .sky-moon {
  inset: auto;
  left: 50%;
  top: 64%;
  border-radius: 50%;
}

.screen-play .sky-sun {
  width: calc(var(--lens) * 0.3);
  height: calc(var(--lens) * 0.3);
  margin: calc(var(--lens) * -0.15);
  background: radial-gradient(circle at 40% 40%, #fff8d6, #ffd65e 55%, #f0a640);
  box-shadow: 0 0 calc(var(--lens) * 0.18) #ffcf5a99;
  transform: translate(calc(cos(var(--ang)) * var(--r)), calc(sin(var(--ang)) * var(--r) * -1));
}

.screen-play .sky-moon {
  --ang-m: calc(var(--ang) + 0.5turn);
  width: calc(var(--lens) * 0.24);
  height: calc(var(--lens) * 0.24);
  margin: calc(var(--lens) * -0.12);
  background: radial-gradient(circle at 35% 35%, #ffffff, #d9e2f5 60%, #a9b8dc);
  box-shadow: 0 0 calc(var(--lens) * 0.16) #bcd0ff66, inset calc(var(--lens) * -0.06) calc(var(--lens) * -0.04) 0 0 #5f6f9e;
  transform: translate(calc(cos(var(--ang-m)) * var(--r)), calc(sin(var(--ang-m)) * var(--r) * -1));
}

.screen-play .sky-dusk {
  background: linear-gradient(transparent 25%, #ff7a3a99 62%, #ffc76e 78%);
  opacity: 0;
  mix-blend-mode: screen;
}

.screen-play .clock.is-turning .sky-dusk {
  animation: clock-dusk-band var(--ms-turn) ease-in-out;
}

.screen-play .sky-ground {
  top: 62%;
  background: linear-gradient(#324a37, #1b291f);
  clip-path: polygon(0 34%, 10% 34%, 10% 18%, 20% 18%, 20% 30%, 28% 30%, 28% 4%, 33% 0, 38% 4%, 38% 26%, 47% 26%, 47% 14%, 55% 14%, 55% 30%, 64% 30%, 64% 20%, 72% 20%, 72% 32%, 82% 32%, 82% 24%, 90% 24%, 90% 34%, 100% 34%, 100% 100%, 0 100%);
  transition: filter var(--ms-turn) ease;
}

.screen-play .clock[data-phase="night"] .sky-ground {
  filter: brightness(0.35) saturate(0.5);
}

/* Windows follow the city's bulbs (lampGlow), 72s after dusk and off 72s before dawn. */
.screen-play .sky-windows {
  top: 62%;
  background:
    radial-gradient(1.4px 1.4px at 14% 60%, #ffd27a, transparent),
    radial-gradient(1.4px 1.4px at 31% 30%, #ffd27a, transparent),
    radial-gradient(1.4px 1.4px at 34% 52%, #ffe39a, transparent),
    radial-gradient(1.4px 1.4px at 51% 44%, #ffd27a, transparent),
    radial-gradient(1.4px 1.4px at 68% 56%, #ffd27a, transparent),
    radial-gradient(1.4px 1.4px at 86% 58%, #ffe39a, transparent);
  opacity: 0;
  transition: opacity 500ms ease;
}

.screen-play .clock[data-lamps="on"] .sky-windows {
  opacity: 1;
}

.screen-play .sky-cloud {
  inset: auto;
  top: 26%;
  left: 0;
  width: 58%;
  height: 20%;
  border-radius: 999px;
  background: #eef4f9;
  box-shadow: inset 0 calc(var(--lens) * -0.05) calc(var(--lens) * 0.06) #9db2c5aa;
  opacity: 0;
  transition: opacity 600ms ease, filter var(--ms-turn) ease;
  animation: clock-cloud-drift 18s linear infinite;
  /* A hidden cloud's drift still ticks on the main thread every frame, so it
     drifts only while it shows. */
  animation-play-state: paused;
}

.screen-play .sky-cloud::before {
  content: "";
  position: absolute;
  left: 24%;
  top: -60%;
  width: 44%;
  height: 140%;
  border-radius: 50%;
  background: inherit;
  box-shadow: inherit;
}

.screen-play .sky-cloud.c2 { top: 44%; width: 44%; height: 16%; animation-duration: 26s; animation-delay: -9s; }
.screen-play .sky-cloud.c3 { top: 10%; width: 52%; height: 18%; animation-duration: 21s; animation-delay: -14s; }
.screen-play .sky-cloud.c4 { top: 34%; width: 62%; height: 20%; animation-duration: 17s; animation-delay: -5s; }
.screen-play .sky-cloud.c5 { top: 20%; width: 40%; height: 15%; animation-duration: 29s; animation-delay: -20s; }
.screen-play .sky-cloud.c6 { top: 50%; width: 56%; height: 17%; animation-duration: 23s; animation-delay: -11s; }

.screen-play .clock:is([data-weather="cloud"], [data-weather="rain"], [data-weather="storm"]) .sky-cloud {
  opacity: 0.95;
  animation-play-state: running;
}

.screen-play .clock:not([data-weather="storm"]) .sky-cloud.storm-only {
  opacity: 0;
  animation-play-state: paused;
}

.screen-play .clock[data-weather="storm"] .sky-cloud {
  filter: brightness(0.5) saturate(0.6);
}

.screen-play .clock[data-phase="night"] .sky-cloud {
  filter: brightness(0.45);
}

.screen-play .clock[data-phase="night"][data-weather="storm"] .sky-cloud {
  filter: brightness(0.3);
}

.screen-play .sky-rain {
  --rain-ms: 0.7s;
  inset: -20%;
  transform: skewX(-12deg);
  mask-image: linear-gradient(transparent 22%, #000 42%, #000 72%, transparent 88%);
  opacity: 0;
  transition: opacity 400ms ease;
}

/* Two sheets of drops, each a period taller than the sky, slid down one
   period a loop by transform so the compositor runs them. They fall only
   while it rains. */
.screen-play .sky-rain::before,
.screen-play .sky-rain::after {
  content: "";
  position: absolute;
  inset: 0;
  top: calc(-1 * var(--p));
}

.screen-play .sky-rain::before {
  --p: 14px;
  background: radial-gradient(0.6px 4.5px at 4.5px 7px, #f0f8ffcc 0, #f0f8ff66 45%, transparent 100%) 0 0 / 9px var(--p);
}

.screen-play .sky-rain::after {
  --p: 19px;
  background: radial-gradient(0.6px 3.5px at 6px 9px, #f0f8ff99 0, #f0f8ff44 45%, transparent 100%) 5px 0 / 12px var(--p);
}

.screen-play .clock:is([data-weather="rain"], [data-weather="storm"]) .sky-rain {
  opacity: 0.5;
}

.screen-play .clock:is([data-weather="rain"], [data-weather="storm"]) .sky-rain::before,
.screen-play .clock:is([data-weather="rain"], [data-weather="storm"]) .sky-rain::after {
  animation: clock-rain-fall var(--rain-ms) linear infinite;
}

.screen-play .clock[data-weather="storm"] .sky-rain {
  --rain-ms: 0.38s;
  opacity: 0.65;
}

.screen-play .sky-shade {
  background: #1d2c3d;
  mix-blend-mode: multiply;
  opacity: 0;
  transition: opacity 600ms ease;
}

.screen-play .clock[data-weather="cloud"] .sky-shade { opacity: 0.3; }
.screen-play .clock[data-weather="rain"] .sky-shade { opacity: 0.5; }
.screen-play .clock[data-weather="storm"] .sky-shade { opacity: 0.68; }

.aspect-portrait .screen-play .clock {
  gap: 8px;
  min-height: 36px;
  padding: 0 8px 0 0;
  --clock-radius: 6px;
}

.aspect-portrait .screen-play .clock-copy {
  padding: 3px 0;
}

.aspect-portrait .screen-play .clock-sky {
  --lens: 36px;
}

.aspect-portrait .screen-play .clock-time {
  font-size: 16px;
}

@keyframes clock-copy-swap {
  0%, 100% { opacity: 1; transform: none; }
  40% { opacity: 0; transform: translateY(3px); }
  55% { opacity: 0; transform: translateY(-3px); }
}

@keyframes clock-dusk-band {
  0%, 100% { opacity: 0; }
  45% { opacity: 1; }
}

@keyframes clock-twinkle {
  from { opacity: 0.55; }
  to { opacity: 1; }
}

@keyframes clock-cloud-drift {
  from { transform: translateX(-110%); }
  to { transform: translateX(190%); }
}

@keyframes clock-rain-fall {
  to { transform: translateY(var(--p)); }
}

.screen-play .menu-bar {
  background: transparent;
}

.screen-play .menu-bar-row {
  padding: 0;
  gap: 0;
  overflow: visible;
  box-shadow: none;
  background: var(--skin-panel);
  border: 0;
  border-top: 5px solid var(--skin-accent);
}

.screen-play .menu-bar button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--control-height);
  min-width: 0;
  padding: 4px 10px;
  border: 1px solid transparent;
  border-right-color: var(--play-line);
  border-radius: 0;
  background: transparent;
  color: var(--text);
  font-size: 14px;
  text-shadow: none;
  filter: none;
  padding-block: 8px;
}

/* Move City Here on a cell's preview: the one action, in gold. */
.screen-play .menu-bar button.visit-move {
  background: var(--btn-positive);
  color: var(--btn-positive-ink);
  text-shadow: none;
  font-weight: 600;
}

.screen-play .menu-bar button.visit-move.holding {
  background: var(--btn-hold-bg);
}

/* The visit's way back (visit_bar.js): the panels' amber Back, holding the
   menu bar's left quarter, a gap before the visit's action. */
.screen-play .menu-bar button.visit-back {
  flex: 0 0 25%;
  margin-right: 8px;
}

/* Attack while scouting a raider city on the map: the one action, in red. */
.screen-play .menu-bar button.visit-attack {
  background: var(--btn-negative);
  color: var(--btn-negative-ink);
  text-shadow: none;
  font-weight: 600;
}

.screen-play .menu-bar .dest-item .i {
  width: 22px;
  height: 22px;
  color: #d5e0f2;
}

/* The shared selected look: the active menu button, the one a notification rises over
   (notifications.js), a pressed filter chip, and the tile a detail shows (a floor plan's chosen
   Build tile). */
.screen-play .menu-bar button.active,
.screen-play .menu-bar button[aria-current="page"],
.screen-play .menu-bar button.is-notified,
.filter-chip[aria-pressed="true"],
.screen-play button.btn.neutral.filter-chip[aria-pressed="true"],
.screen-play button.btn.neutral.filter-chip[aria-checked="true"],
.screen-play .inv-tile.selected,
.screen-play .inv-tile.selected:hover {
  color: var(--selected-ink);
  background: var(--selected-fill);
  border-color: var(--selected-line);
  box-shadow: var(--selected-glow);
}

/* A destination with something needing the player keeps its button red: Radio with a call, the Logbook with an Action. */
.screen-play .menu-bar button.needs-attention {
  background: var(--btn-negative);
  color: var(--btn-negative-ink);
  text-shadow: none;
}

.screen-play .menu-bar button.active .i,
.screen-play .menu-bar button[aria-current="page"] .i,
.screen-play .menu-bar button.is-notified .i {
  color: var(--gold-200);
}

.screen-play .menu-bar button.needs-attention .i {
  color: var(--btn-negative-ink);
}

html.aspect-portrait .screen-play .menu-bar {
  padding: 0;
}

html.aspect-portrait .screen-play .top-bar {
  margin: 0;
  padding: 4px 8px 5px;
  border-top: 0;
  border-left: 0;
  border-right: 0;
  border-radius: 0;
}

html.aspect-portrait .screen-play .chips {
  gap: 6px;
}

html.aspect-portrait .screen-play .res {
  --res-icon-size: 18px;
  gap: 0 3px;
}

html.aspect-portrait .screen-play .res-name {
  font-size: 9px;
  letter-spacing: .04em;
}

html.aspect-portrait .screen-play .res-line {
  gap: 3px;
}

html.aspect-portrait .screen-play .res-val {
  font-size: 16px;
}

html.aspect-portrait .screen-play .res-flag {
  font-size: 10px;
}

html.aspect-portrait .screen-play .menu-bar-row {
  width: 100%;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  border-bottom: 0;
}

html.aspect-portrait .screen-play .menu-bar .dest-item {
  flex-direction: column;
  gap: 3px;
}

html.aspect-portrait .screen-play .menu-bar button {
  min-height: 50px;
  padding: 5px 4px;
  font-size: 12px;
}

.screen-play .tool {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: var(--control-height);
  padding: 0 10px;
  border-radius: var(--control-radius);
  border: 1px solid var(--play-line-strong);
  background: var(--btn-neutral-face);
  color: var(--text);
  font-size: 14px;
  box-shadow: var(--shadow-card);
  text-shadow: 0 2px 2px #000;
}

:where(.aspect-portrait) .screen-play .build-tool {
  min-height: calc(2 * var(--control-height));
}

.screen-play .debug-btn {
  width: 24px;
  height: 24px;
  align-self: flex-end;
}

.screen-play .debug-btn.active {
  border-color: #80a8b4;
  background: linear-gradient(#304955, #1b3038);
}

.screen-play .panel.sheet {
  padding: 8px 14px 10px;
  background: var(--surface-panel);
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  box-shadow: var(--shadow-sheet);
  color: var(--text);
}

:is(.screen-play, .screen-title, .screen-designer) .panel h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--panel-title-size);
  line-height: 1.1;
  color: var(--text);
  text-shadow: 0 2px 2px #000a;
}

html.aspect-portrait .screen-play .panel.sheet {
  width: 100%;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
}

.screen-play .bottom-panel {
  padding-top: calc(var(--panel-pad) + var(--icon-control-size) + 12px);
}

/* Bottom panels hold one height whatever tab or content is showing: the space between the
   top row and the menu bar, capped at --panel-fixed-max. The tab strip stays put when the pane changes. */
.screen-play .bottom-panel .panel {
  flex: 0 0 auto;
  height: min(100%, var(--panel-fixed-max));
}

:is(.screen-play .bottom-panel, :is(.screen-title, .screen-designer) .menu-box) .panel {
  background: var(--surface-panel);
  border: 0;
  border-top: 5px solid var(--skin-accent);
  border-radius: var(--panel-radius);
  box-shadow: var(--shadow-sheet);
}

:is(.screen-title, .screen-designer) .menu-box .panel {
  padding: var(--panel-inset);
}

/* One inset around whatever a bottom sheet holds. The top strip (.panel-tabline) is
   absolutely positioned, so the padding only moves the content. */
.screen-play .bottom-panel .panel.sheet {
  padding: var(--panel-inset);
}

/* Every tab pane scrolls the same way: the pane is the scroller, with a thin bar that
   keeps its gutter so rows sit at the same width whether or not the pane overflows. */
.screen-play .panel-pane {
  scrollbar-width: thin;
  scrollbar-color: var(--scroll) transparent;
  scrollbar-gutter: stable;
  padding-right: 6px;
}

.screen-play #alliance-panel:not(.alliance-notices-read) .alliance-notice-list-scroll {
  overflow: visible;
  flex: 0 0 auto;
}

.screen-play .alliance-notice-list {
  padding-right: 0;
}

.screen-play .member-copy {
  min-width: 0;
}

.screen-play .account-chip,
.screen-play .debug-btn {
  background: var(--btn-neutral-face);
  color: var(--text);
  border: 1px solid var(--play-line-strong);
  border-radius: var(--control-radius);
}

.screen-play .menu-bar button:first-child { border-radius: var(--panel-radius-inner) 0 0 var(--panel-radius-inner); }
.screen-play .menu-bar button:last-child { border-radius: 0 var(--panel-radius-inner) var(--panel-radius-inner) 0; }

.screen-play .member {
  display: grid;
  grid-template-columns: 80px minmax(0, 1fr);
  gap: 12px;
  padding: 6px 3px;
  margin: 0;
  border-top: 1px solid var(--play-line);
  color: var(--text);
  cursor: pointer;
}

.screen-play .look {
  position: relative;
  width: 80px;
  height: 82px;
  border-radius: 3px;
  background: var(--portrait-ground);
  overflow: hidden;
}

.screen-play .look canvas,
.screen-play .look img {
  width: 100%;
  height: 100%;
  display: block;
}

/* Settler profile (panels/settler_profile.js): the whole portrait beside the
   name line, then the XP budget and one card per skill. Phones stack it. */
.screen-play .settler-profile-top {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
}

/* settler_profile.js sets the aspect from the figure it frames. */
.screen-play .settler-profile-portrait {
  position: relative;
  width: 180px;
  border-radius: 3px;
  background: var(--portrait-ground);
  overflow: hidden;
}

.screen-play .settler-profile-head,
.screen-play .settler-profile-afflictions,
.screen-play .settler-profile-xp,
.screen-play .settler-skill {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.screen-play .settler-profile-afflictions {
  align-items: flex-start;
}

.screen-play .settler-profile-xp {
  margin-top: 14px;
  gap: 8px;
}

.screen-play .settler-skill-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.screen-play .settler-skill-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
}

.screen-play .settler-skill-tag {
  font-family: var(--font-ui);
  font-size: var(--meta-text-size);
}

/* The amber XP still needed, after a skill's title. */
.screen-play .settler-skill-togo {
  color: var(--warn);
  font-family: var(--font-ui);
  font-size: var(--meta-text-size);
  font-weight: 500;
}

.screen-play .settler-skill-xp {
  margin-left: auto;
}

/* A skill's bar: one step per level, each as wide as its share of the XP to
   the top level (--span). */
.screen-play .level-steps {
  display: flex;
  gap: 3px;
}

.screen-play .level-steps .meter {
  flex: var(--span) 1 0;
  min-width: 4px;
}

.screen-play .settler-skill-earn {
  margin: 0;
  padding-left: 1.2em;
  color: var(--muted);
  font-size: var(--meta-text-size);
}

.screen-play .settler-skill-how {
  color: var(--muted);
  font-size: var(--caption-text-size);
}

.screen-play .settler-skill-warn {
  color: var(--warn);
  font-size: var(--meta-text-size);
}

.screen-play .member-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.screen-play .member-name {
  margin: 0;
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 20px;
  line-height: 1.1;
  color: var(--text);
  text-shadow: 0 2px 2px #0009;
}

.screen-play .member-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 1 auto;
  order: 0;
  flex-wrap: wrap;
}

.screen-play .member-meta {
  margin-left: auto;
  order: 1;
  color: var(--muted);
  font-size: var(--meta-text-size);
  white-space: nowrap;
}

.screen-play .pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: var(--compact-control-height);
  padding: 0 8px;
  border-radius: 5px;
  border: 1px solid #52997766;
  background: linear-gradient(#28543cbb, #173d2c99);
  color: var(--ok-text);
  font-size: 13px;
  white-space: nowrap;
}

.screen-play .xp-line {
  margin-block: 2px;
  color: #c4dbef;
  font-size: 14px;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.screen-play .traits {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

.screen-play .trait {
  flex: 1 1 auto;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  min-height: var(--compact-control-height);
  padding: 0 8px;
  border-radius: 5px;
  border: 1px solid var(--play-line);
  background: linear-gradient(110deg, #21343d, #1c2c35);
  color: var(--text);
  font-size: 13px;
  white-space: nowrap;
  text-shadow: 0 1px 2px #000;
}

.screen-play .trait::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--fill, 0) * 100%);
  background: linear-gradient(#8e743c99, #67532a99);
  z-index: -1;
  pointer-events: none;
}

.screen-play .trait[data-tone="alert"]::before {
  background: color-mix(in srgb, var(--alert) 30%, transparent);
}

.screen-play .trait.is-locked {
  color: var(--muted);
}

.screen-play .meter {
  height: 10px;
  border-radius: 999px;
  border: 1px solid var(--well-line);
  background: var(--surface-well);
  box-shadow: inset 0 1px 3px #0005;
  overflow: hidden;
}

.screen-play .meter > span {
  display: block;
  height: 100%;
  width: var(--fill, 0%);
  border-radius: inherit;
  background: var(--meter-fill);
  box-shadow: inset 0 1px 2px #ffffddbb;
  transition: width 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.screen-play .meter[data-tone="info"] > span {
  background: linear-gradient(#9ce8ff, #56b8ed 60%, #589ddb);
}

.screen-play .meter[data-tone="alert"] > span {
  background: linear-gradient(#ffb1b5, #d96c77);
}

.screen-play .card,
.screen-play .city-sheet.card,
.screen-play .city-sheet-card,
.screen-play .map-cell-sheet {
  background: var(--surface-panel);
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  box-shadow: var(--shadow-card);
  color: var(--text);
}

.screen-play .city-sheet-card {
  display: grid;
  grid-row: 1 / 3;
  grid-template-rows: subgrid;
  width: auto;
  position: relative;
  min-width: 0;
  padding: 0;
  box-shadow: none;
  background: var(--skin-panel);
  border: 0;
  border-left: 6px solid var(--tone, var(--skin-accent));
}

.city-sheet-content { grid-row: 2; padding: var(--city-sheet-content-pad); }
:where(.city-sheet-card:has(.card-status[hidden])) .city-sheet-content { grid-row: 1 / 3; }

.screen-play .card[data-tone="info"] { --tone: var(--info); border-color: var(--tone); }
.screen-play .card[data-tone="ready"] { --tone: var(--gold-200); border-color: var(--tone); }
.screen-play .card[data-tone="alert"] { --tone: var(--alert); border-color: var(--tone); }
.screen-play .card[data-tone="warn"] { --tone: var(--warn); border-color: var(--tone); }
.screen-play .card[data-tone="ok"] { --tone: var(--ok); border-color: var(--tone); }

.city-sheet-tools .icon-btn {
  width: var(--city-sheet-tool-width);
  height: auto;
  min-height: var(--city-sheet-tool-width);
}

.city-sheet-tools .city-sheet-upgrade {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  height: auto;
  min-height: calc(var(--city-sheet-tool-width) * 2);
  padding: 6px 2px;
  font-family: var(--font-ui);
  text-shadow: none;
}

.city-sheet-upgrade[data-ready="true"] {
  --btn-neutral-face: var(--btn-go);
  --play-line-strong: var(--ok);
  --text: var(--btn-go-ink);
}

.city-sheet-level-caption { font-size: var(--caption-text-size); line-height: 1; }
.city-sheet-level-value { font-size: 28px; font-weight: 600; line-height: 1; }

/* The inventory's titled boxes (inventory_box.js): Consumables' food and fuel, Troops' clankers
   and troops. */
.screen-play .inv-box {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
}

.screen-play .inv-box-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
}

.screen-play .consumable-box {
  --chip-fill: var(--tone);
  position: relative;
}

.screen-play .consumable-rates {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px 12px;
  margin: 0;
}

.screen-play .consumable-rates dt {
  font-family: var(--font-ui);
  font-size: var(--caption-text-size);
  color: var(--muted);
}

.screen-play .consumable-rates dd {
  margin: 0;
  font-family: var(--font-ui);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.screen-play .consumable-rows {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--meta-text-size);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.screen-play .consumable-note {
  margin: 0;
  font-size: 14px;
}

.screen-play .consumable-tips {
  margin: 0;
  padding-left: 18px;
  font-size: 14px;
}

.screen-play .consumable-lead {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--tone);
}

.screen-play .production-orbit { display: none; }

.screen-play .card[data-producing] > .production-orbit {
  display: block;
  position: absolute;
  inset: -2px;
  padding: 2px;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
}

.screen-play .production-orbit::before {
  content: "";
  position: absolute;
  inset: -50%;
  background: conic-gradient(from 45deg, transparent 0deg 275deg, #78d5fa20 300deg, #a5e8ff80 330deg, #e3faffcc 345deg, transparent 360deg);
  animation: production-orbit 8s linear infinite;
}

.screen-play .card[data-producing] .meter > span {
  position: relative;
  overflow: hidden;
}

.screen-play .card[data-producing] .meter > span::after,
.screen-play button.btn.processing::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 40%;
  background: linear-gradient(90deg, transparent, #ffffff4d, transparent);
  transform: translateX(-150%) skewX(-20deg);
  opacity: 0;
  pointer-events: none;
  animation: activity-shimmer 5.5s ease-in-out infinite;
}

.screen-play button.btn.processing::after {
  animation-duration: 2s;
  animation-delay: -.8s;
}

.screen-play button.btn.processing:disabled { filter: none; }

.screen-play button.btn.holding {
  background: var(--btn-hold-bg);
}

.screen-play button.btn.holding .btn-fill {
  transform: scaleX(var(--btn-hold-t));
}

@keyframes production-orbit { to { transform: rotate(360deg); } }

@keyframes activity-shimmer {
  0%, 38% { transform: translateX(-150%) skewX(-20deg); opacity: 0; }
  46%, 68% { opacity: 1; }
  78%, 100% { transform: translateX(400%) skewX(-20deg); opacity: 0; }
}

.screen-play .card-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -8px -12px 5px;
  padding: 4px 12px 4px 6px;
  border-bottom: 1px solid var(--tone, var(--play-line));
  background: color-mix(in srgb, var(--tone, var(--info)) 10%, transparent);
  color: var(--tone, var(--text));
  font-weight: 500;
  font-size: 14px;
  line-height: 18px;
  border-radius: 0;
}

.screen-play .card[data-tone="ok"] .card-status {
  color: var(--ok-text);
}

.city-sheet-card .card-status { grid-row: 1; margin: 0; }
.city-sheet-line { flex: 1; min-width: 0; }
.city-sheet-eta {
  flex: none;
  margin-left: auto;
  color: var(--text);
  font-size: 22px;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.city-sheet-eta[data-never="true"] { color: var(--alert); }

/* A turret base under its weapon system and the weapon's Manage: the card's
   foot, set off from the weapon above like the head is from the title. */
.screen-play .city-sheet-base {
  border-top: 1px solid var(--tone, var(--play-line));
  background: color-mix(in srgb, var(--surface-0) 70%, transparent);
  margin: 8px -18px -12px;
  padding: 12px 18px;
  border-radius: 0;
}

/* The head's badge is the building's pip, opened up: it keeps the pip's tone. */
.screen-play .card-pip {
  display: flex;
  flex: none;
  color: var(--tone);
}

.screen-play .card-title,
.screen-play .city-sheet-title,
.screen-play .map-cell-sheet-title,
.screen-play .top-panel-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--card-title-size);
  line-height: 1.1;
  color: var(--text);
  text-shadow: 0 2px 2px #000;
}

.screen-play .city-sheet-title {
  width: max-content;
  white-space: nowrap;
  font-size: 30px;
  text-transform: uppercase;
}

.city-sheet-card .city-sheet-title {
  width: auto;
  min-width: 0;
  white-space: normal;
}

.screen-play .card-meta,
.screen-play .city-sheet-sub {
  margin: 4px 0 0;
  font-family: var(--font-ui);
  font-size: var(--meta-text-size);
  line-height: 1.1;
  color: var(--muted);
}

.screen-play .card-line {
  margin: 3px 0 5px;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.screen-play :is(.city-sheet, .city-sheet-card) .meter {
  height: 12px;
}

/* Site card: a bar per carried resource. A site bar is a
   segment per load, filled as the load lands. Out bars have nothing left to
   carry. Columns: name, meter, quantity. Crew slots share the meter's column. */
.city-sheet-work {
  display: none;
  grid-template-columns: max-content 1fr max-content;
  align-items: center;
  gap: 5px var(--city-sheet-gap);
  margin: 6px 0 2px;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.1;
}

.city-sheet-work:where(:has(> :not([hidden]))) { display: grid; }

.screen-play .city-sheet-site { display: contents; }

.screen-play .city-sheet-site[hidden] {
  display: none;
}

.screen-play :is(.site-bars, .site-bar) {
  display: contents;
}

.screen-play .site-bar-name {
  color: var(--text);
}

.screen-play :is(.city-sheet, .city-sheet-card) .site-bar-meter {
  height: 8px;
}

.screen-play .site-bar-qty {
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* Out: the unfilled track is hatched, so the missing part reads as stuck. */
.screen-play .site-bar[data-outlook="out"] .site-bar-meter:not([data-segs="true"]) {
  border-color: color-mix(in srgb, var(--alert) 60%, transparent);
  background: repeating-linear-gradient(135deg, #3a2328 0 4px, var(--surface-2) 4px 8px);
}

.screen-play .site-bar[data-outlook="out"] .site-bar-meter:not([data-segs="true"]) > span,
.screen-play .site-bar[data-outlook="out"] .site-bar-meter[data-segs="true"] > span[data-done="true"] {
  background: linear-gradient(#ffb1b5, #d96c77);
}

.screen-play .site-bar[data-outlook="out"] .site-bar-meter[data-segs="true"] > span[data-done="false"] {
  border-color: color-mix(in srgb, var(--alert) 60%, transparent);
  background: repeating-linear-gradient(135deg, #3a2328 0 4px, var(--surface-2) 4px 8px);
}

.screen-play .site-bar[data-outlook="out"] :is(.site-bar-name, .site-bar-qty) {
  color: var(--alert);
}

/* A segmented bar drops the meter chrome and draws a segment per load, gold
   once that load has landed. */
.screen-play .site-bar-meter[data-segs="true"] {
  display: flex;
  gap: 2px;
  height: 10px;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  overflow: visible;
}

.screen-play .site-bar-meter[data-segs="true"] > span {
  flex: 1;
  width: auto;
  height: auto;
  min-width: 2px;
  border-radius: 2px;
  border: 1px solid var(--well-line);
  background: var(--surface-well);
  box-shadow: none;
  transition: none;
}

.screen-play .site-bar-meter[data-segs="true"] > span[data-done="true"] {
  border-color: transparent;
  background: var(--meter-fill);
}

.screen-play :is(.city-sheet, .city-sheet-card) .card-actions,
.screen-play #city-sheet-craft {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.screen-play :is(.city-sheet, .city-sheet-card) button:not(.seat):not(.icon-btn),
.screen-play .map-cell-sheet button {
  display: flex;
  width: 100%;
  min-height: var(--control-height);
}

/* Buttons are block-level, so each sheet sizes to its widest row, not to its
   buttons laid end to end, and a label change cannot move it. */
.screen-play .map-cell-sheet {
  min-width: 220px;
}

.screen-play :is(.city-sheet, .city-sheet-card) .card-actions button {
  margin-top: 0;
}

.city-sheet-actions {
  display: none;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--city-sheet-gap);
  margin-top: 12px;
}

.city-sheet-actions:where(:has(> button:not([hidden]))) { display: grid; }
/* Each button spans the row, but Cancel or Discard, the negative one, takes
   the right half beside Manage. */
.city-sheet-actions > :where(button) { grid-column: 1 / -1; }
:is(.city-sheet-cancel, .city-sheet-discard) { grid-column: 2; }
:where(.city-sheet-actions:has(> :is(.city-sheet-cancel, .city-sheet-discard):not([hidden]))) > .city-sheet-manage { grid-column: 1; }

.screen-play .radio-host,
.screen-play .radio-dock {
  background: var(--surface-panel);
  color: var(--text);
}

.screen-play .chat-thread-header h2,
.screen-play .chat-room,
.screen-play .chat-bubble-name,
.screen-play .chat-bubble-body {
  font-family: var(--font-body);
  color: var(--text);
}

.screen-play .chat-bubble-name {
  font-weight: 600;
  font-size: 14px;
  text-box-trim: trim-start;
  text-box-edge: cap alphabetic;
  text-transform: uppercase;
  letter-spacing: .06em;
}

.screen-play .chat-bubble-body {
  font-weight: 400;
  font-size: 14px;
  line-height: 1.48;
}

.screen-play .chat-thread {
  --chat-inset: 12px;
  padding: 10px 0;
}

.screen-play .chat-msg {
  padding: 0 var(--chat-inset) 8px;
  border-bottom: 0;
  padding-bottom: 0;
}

/* The developer's messages: a tinted row with an edge in its colour, which no player's name can fake.
   The tint runs up into the gap above it, where the line above it would only repeat the edge. */
.screen-play .chat-msg.developer {
  margin-top: calc(-1 * var(--chat-stack-gap));
  padding-top: var(--chat-stack-gap);
  background: color-mix(in srgb, var(--developer) 11%, transparent);
  box-shadow: inset 3px 0 0 var(--developer);
}

.screen-play .chat-msg:has(+ .chat-msg.developer) {
  border-bottom-color: transparent;
}

/* The System and the Developer stand in a crest's place with an icon on their own colour. */
.chat-avatar-system,
.chat-avatar-developer {
  --author: var(--developer);
  display: grid;
  place-items: center;
  height: var(--chat-avatar);
  border-radius: 8px;
  background: color-mix(in srgb, var(--author) 24%, transparent);
  color: var(--author);
}

.chat-avatar-system {
  --author: var(--system);
}

.chat-avatar-system .i,
.chat-avatar-developer .i {
  width: 60%;
  height: 60%;
}

.chat-room-face .i {
  width: 80%;
  height: 80%;
}

.screen-play .chat-msg:has(+ .chat-msg.continued) {
  padding-bottom: 0;
  border-bottom: 0;
}

.screen-play .chat-msg.continued {
  padding-left: calc(var(--chat-inset) + var(--chat-avatar) + var(--chat-msg-gap));
}

.screen-play .chat-bubble,
.screen-play .chat-bubble.mine {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 4px 0;
  color: var(--text);
}

.screen-play .chat-composer input {
  min-height: var(--control-height);
}

.screen-play .chat-composer button {
  min-width: 76px;
  min-height: var(--control-height);
}

.screen-play .bottom-panel {
  --panel-tabline-height: 54px;
}

.screen-play .bottom-panel:has(.panel-tabs:not([hidden])) {
  padding-top: calc(var(--panel-pad) + var(--panel-tabline-height) + 12px);
}

/* Every bottom sheet's top strip: its tabs when two or more show, then the
   Back (button.js bindBack) when the view has one, then the close. With no
   tabs showing it shrinks to Back and close at the top right; with tabs it
   spans the sheet and Back and close line up with the selected tab's top. */
.screen-play .panel-tabline {
  position: absolute;
  top: calc(-1 * (var(--icon-control-size) + 12px));
  right: 0;
  height: var(--icon-control-size);
  display: flex;
  gap: 8px;
  pointer-events: none;
  padding-inline: var(--play-control-inset);
}

.screen-play .panel-tabline:has(.panel-tabs:not([hidden])) {
  --panel-tabline-lift: calc(var(--panel-tab-selected-height) - var(--icon-control-size));
  top: calc(-1 * var(--panel-tabline-height));
  left: 0;
  height: var(--panel-tabline-height);
  align-items: flex-end;
}

.screen-play .panel-tabline > button {
  margin-bottom: var(--panel-tabline-lift, 0);
  pointer-events: auto;
}

.screen-play .panel-tabs {
  flex: 1;
  height: var(--panel-tabline-height);
  gap: 0;
  align-items: flex-end;
  pointer-events: auto;
  margin-left: 0;
}

.screen-play .panel-tabs[hidden] {
  display: none;
}

:is(.screen-play, .screen-designer) .panel-tab {
  z-index: 0;
  min-height: calc((var(--control-height) + var(--panel-tab-selected-height)) / 2);
  padding: 0 12px;
  background: transparent;
  font-family: var(--font-ui);
  font-size: var(--control-text-size);
  pointer-events: auto;
  --tab-ground: var(--skin-accent);
  --tab-shadow: none;
  border-radius: 0;
  box-shadow: none;
  text-shadow: none;
  border: 0;
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--skin-panel);
}

:is(.screen-play, .screen-designer) .panel-tab::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--tab-ground);
  box-shadow: var(--tab-shadow);
  backdrop-filter: blur(12px) saturate(.75);
  pointer-events: none;
}

:is(.screen-play, .screen-designer) .panel-tab:hover {
  --tab-ground: var(--skin-selected);
  color: var(--text);
}

:is(.screen-play, .screen-designer) .panel-tab:not([hidden]) ~ .panel-tab:not([hidden]) {
  margin-left: -1px;
}

:is(.screen-play, .screen-designer) .panel-tab[aria-selected="true"] {
  --tab-shadow: none;
  z-index: 4;
  height: var(--panel-tab-selected-height);
  border-color: var(--panel-line);
  border-bottom-color: #263638;
  --tab-ground: var(--skin-text);
  color: var(--skin-panel);
}

.screen-play .panel-tab:has(:where(.badge:not([hidden]))) {
  z-index: 5;
}

.screen-play .badge {
  z-index: 2;
  background: #ff3340;
}

/* Every typed field in play: text-like inputs, selects, and textareas.
   Checkbox and range have their own skins. */
:is(.screen-play, .screen-title, .screen-designer) :is(input:not([type="checkbox"], [type="range"], [type="radio"], [type="file"], [type="color"], [type="button"], [type="submit"], [type="reset"], [type="image"], [type="hidden"]), select, textarea) {
  border: 1px solid var(--play-line);
  border-radius: var(--control-radius);
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font-body);
}

.screen-title .password-field {
  background: var(--surface-2);
  border-color: var(--play-line);
}

.screen-title .password-field input {
  border: 0;
  background: transparent;
}

/* A card is the play skin's panel with its tone on a left edge, as a building card wears its
   status: gold, green when positive, red when negative. */
.screen-play .notification {
  --notice-line: var(--skin-accent);
  border: 0;
  border-left: 6px solid var(--notice-line);
  border-radius: var(--panel-radius);
  background: var(--skin-panel);
  color: var(--text);
  box-shadow: var(--shadow-card);
}

.screen-play .notification.positive {
  --notice-line: var(--ok);
}

.screen-play .notification.negative {
  --notice-line: var(--alert);
}

@media (max-width: 600px) {
  .screen-play {
    --control-height: 28px;
    --icon-control-size: 28px;
    --meta-text-size: 13px;
    --caption-text-size: 11px;
  }
  .screen-play .panel.sheet { padding: 8px 10px; }
  /* Tabs size to their labels so four (Goods, Consumables, Troops, Inventory)
     fit a phone without clipping the long one. */
  .screen-play .panel-tab { flex: 1 1 auto; padding: 0 8px; white-space: nowrap; }
  .screen-play .member { grid-template-columns: 60px minmax(0, 1fr); gap: 8px; }
  .screen-play .look { width: 60px; height: 66px; }
  .screen-play .settler-profile-top { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  .screen-play .settler-profile-head { justify-self: stretch; }
  .screen-play .member-actions { flex-basis: 100%; order: 2; }
  .screen-play .member-top { gap: 6px; }
  .screen-play .xp-line { margin-block: 4px; }
  .screen-play .traits { gap: 5px; }
  .screen-play .trait { padding: 0 6px; }
  .screen-play .pill { padding: 0 6px; }
  .screen-play .clock { gap: 8px; min-height: 36px; padding: 0 8px 0 0; --clock-radius: 6px; }
  .screen-play .clock-copy { padding: 3px 0; }
  .screen-play .clock-sky { --lens: 36px; }
  .screen-play .clock-time { font-size: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .notifications .notification,
  .notifications.is-open .notification,
  .notifications.is-leaving .notification {
    transform: none;
    transition: none;
  }
  .notification.is-shake { animation: none; }
  .screen-play button.btn.processing::after { animation: none; }
  .screen-play button.btn.holding {
    animation: none;
  }

  .screen-play .clock,
  .screen-play .clock-sky,
  .screen-play .sky-night,
  .screen-play .sky-stars,
  .screen-play .sky-ground,
  .screen-play .sky-windows,
  .screen-play .sky-cloud,
  .screen-play .sky-rain,
  .screen-play .sky-shade {
    transition-duration: 200ms;
    transition-delay: 0s;
    animation: none;
  }
  .screen-play .clock[data-phase] .sky-stars::before,
  .screen-play .clock[data-weather] .sky-rain::before,
  .screen-play .clock[data-weather] .sky-rain::after {
    animation: none;
  }
  .screen-play .clock.is-turning .clock-sky {
    transition-duration: 0s;
  }
  .screen-play .clock.is-turning .clock-copy,
  .screen-play .clock.is-turning .sky-dusk {
    animation: none;
  }

  .screen-play .production-orbit,
  .screen-play .production-orbit::before,
  .screen-play .card[data-producing] .meter > span::after,
  .screen-play .meter > span {
    animation: none;
    transition: none;
  }
}

/* Battle device knobs (debug mode, while a battle is joined). */
.battle-debug {
  position: absolute;
  left: 12px;
  bottom: 96px;
  z-index: 40;
  display: grid;
  gap: 6px;
  padding: 8px 12px;
  background: var(--chrome);
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  pointer-events: auto;
}

.battle-debug-title {
  font-weight: 600;
}

.battle-debug-row {
  display: grid;
  grid-template-columns: 4.5em 8em 4em;
  align-items: center;
  gap: 6px;
}

.battle-debug-buttons {
  display: flex;
  gap: 6px;
}

.battle-debug-buttons button {
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  font: inherit;
  padding: 2px 8px;
}

/* Sarah world playground (/#sarah-world) */
.sarah-world {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  flex-direction: column;
  background: #c9ccd0;
  color: #20282c;
  font: 14px system-ui,sans-serif;
}

.sarah-world-toolbar {
  padding: 14px 18px;
  background: #f3f3f0;
  border-bottom: 1px solid #a7aaac;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.sarah-world-toolbar strong {
  font-size: 18px;
}

.sarah-world-toolbar [data-state] {
  font-weight: 400;
  font-size: 13px;
  color: #526268;
  margin-left: 12px;
}

.sarah-world-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.sarah-world-controls label {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 7px;
  font-size: inherit;
}

.sarah-world-controls input {
  accent-color: #34656a;
}

.sarah-world-controls button,.sarah-world-controls select {
  background: #fff;
  color: #253235;
  border: 1px solid #a4adae;
  border-radius: 4px;
  padding: 7px 10px;
  font: inherit;
  cursor: pointer;
}

.sarah-world-controls button[aria-pressed=true] {
  background: #dce8e6;
}

.sarah-world-view {
  flex: 1;
  min-height: 0;
  position: relative;
}

.sarah-world-view canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

.sarah-world-caption {
  position: absolute;
  bottom: 14px;
  left: 18px;
  color: #30383c;
  padding: 6px 10px;
  border: 1px solid #afb3b6;
  border-radius: 4px;
  background: #eef0f0d9;
  pointer-events: none;
  font-size: 12px;
}

@media (max-width: 560px) {
  .sarah-world-toolbar { padding: 10px 12px; gap: 8px; }
  .sarah-world-controls { gap: 8px; font-size: 12px; }
  .sarah-world-controls button,
  .sarah-world-controls select { padding: 6px; }
}

/* Walker playground (/#walker) */
.walker-playground {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #c9ccd0;
}

.walker-playground canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

.walker-panel {
  position: absolute;
  top: 12px;
  left: 12px;
  width: 240px;
  max-height: calc(100% - 24px);
  overflow-y: auto;
  padding: 12px;
  display: grid;
  gap: 8px;
  background: var(--chrome);
  border-radius: 8px;
  box-shadow: var(--shadow);
  font: 13px var(--font);
  color: var(--ink);
}

.walker-panel label,
.walker-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  font-size: inherit;
}

.walker-panel input {
  padding: 0;
  min-width: 0;
}

.walker-panel select {
  font-size: inherit;
}

.walker-panel button {
  padding: 4px 10px;
}

.walker-panel label:has(input[type="range"]) {
  display: grid;
  grid-template-columns: 64px 1fr 36px;
}

.walker-panel output {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The VFX playground's health panel sits top right, and along the bottom on a narrow screen. */
.vfx-health {
  left: auto;
  right: 12px;
  width: min(320px, calc(100% - 24px));
}

.vfx-health progress {
  flex: 1;
  min-width: 0;
}

.vfx-health output {
  white-space: nowrap;
}

/* The VFX playground's replay bar runs the width of the screen's foot, its
   slider taking all the room the play button and time leave. */
.vfx-replay {
  top: auto;
  right: 12px;
  bottom: 12px;
  width: auto;
  display: flex;
  align-items: center;
  gap: 12px;
}

.vfx-replay input {
  flex: 1;
}

.vfx-replay output {
  min-width: 4em;
}

/* Each benchmark run's stats, a block of lines a run. */
.vfx-runs {
  display: grid;
  gap: 8px;
  font-variant-numeric: tabular-nums;
}

.vfx-runs p {
  margin: 0;
}

/* On a narrow screen the health panel sits along the bottom, above the
   replay bar. */
@media (max-width: 600px) {
  .vfx-health {
    top: auto;
    bottom: 72px;
    left: 12px;
  }
}

.walker-legs {
  display: flex;
  gap: 4px;
}

.walker-legs button[aria-pressed="true"] {
  background: var(--accent);
}

.walker-ui-toggle {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 4px 10px;
  border-radius: 6px;
  box-shadow: var(--shadow);
}

.walker-bare .walker-panel {
  display: none;
}

/* Constructing playground (/#constructing): a build button over each empty plot */
.constructing-plot {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 4px 10px;
  border-radius: 6px;
  box-shadow: var(--shadow);
  white-space: nowrap;
}

.walker-bare .walker-ui-toggle {
  opacity: 0.35;
}

.walker-bare .walker-ui-toggle:hover {
  opacity: 1;
}

.walker-readout,
.walker-hint {
  margin: 0;
  color: var(--muted);
}

/* Lootbox playground (/#lootboxes): a still of the game behind, and the game's own find overlay over it. */
.lootbox-playground {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #0c1416;
}

.lootbox-screen.screen-play {
  display: block;
  height: 100%;
  pointer-events: auto;
}

.lootbox-game {
  position: absolute;
  inset: 0;
  background: center / cover no-repeat;
}

.lootbox-menu {
  position: absolute;
  top: 12px;
  left: 12px;
  display: grid;
  gap: 8px;
  font-family: var(--font-ui);
}

.lootbox-picks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.lootbox-refuse {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.lootbox-status {
  margin: 0;
  font-size: 13px;
  color: #eee0bf;
}

.lootbox-status:empty {
  display: none;
}

.city-find-stage[data-opener="container"] canvas { cursor: crosshair; }
.city-find-stage[data-opener="safe"] canvas { cursor: pointer; }

/* Crafting playground (/#crafting): the controls down the left; the run on the right, a table with the board on it
   and the status below. The board is Forward's layout: 3:4 cards a ninth of their width apart, the settler's card at
   the bottom, the board sliding down as they climb. Every card size reads --cw, the card width the board sets. One
   column at phone width. */
.crafting-playground {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

.crafting-screen.screen-play {
  height: 100%;
  grid-template-columns: 300px minmax(0, 1fr);
  grid-template-rows: auto;
  grid-template-areas: none;
  align-items: start;
  gap: 16px;
  padding: 16px;
  overflow-y: auto;
  pointer-events: auto;
  background: var(--surface-0);
  --craft-felt: #2c2721;
  --craft-paper: linear-gradient(165deg, #f6ecd8, #e7d9bc);
  --craft-ink: #2d2720;
  --craft-edge: #dccfb2;
  --craft-back: #1e1b18;
  --craft-flaw: #8a4cc6;
  --craft-part: #2f9c8c;
  --craft-clamp: #7f8d9b;
  --craft-mend: #c7772f;
  --craft-rule: #d6a330;
  --craft-curse: #5f7d2c;
  --craft-test: #c6a45a;
  --craft-hurt: #c8323c;
  --craft-lift: 0 14px 26px #000a;
}

.craft-side,
.craft-main {
  display: grid;
  gap: 12px;
  min-width: 0;
}

/* The run is one screen tall: the blueprint, then the table taking what is left, then the status. From 1200px the
   blueprint stands beside the table instead, and the table has the full height. */
.craft-main {
  max-width: 720px;
  height: calc(100dvh - 32px);
  grid-template-rows: auto minmax(360px, 1fr) auto;
}

@media (min-width: 1200px) {
  .craft-main {
    max-width: 1060px;
    grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas: "table blueprint" "status blueprint";
  }

  .craft-blueprint {
    grid-area: blueprint;
    align-self: start;
  }

  .craft-table {
    grid-area: table;
  }

  .craft-status {
    grid-area: status;
  }
}

/* At phone width the run comes first and the controls follow. */
@media (max-width: 720px) {
  .crafting-screen.screen-play {
    grid-template-columns: minmax(0, 1fr);
  }

  .craft-main {
    order: -1;
  }
}

.craft-panel {
  display: grid;
  gap: 8px;
  padding: 12px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-panel);
  box-shadow: var(--shadow-card);
}

.craft-heading {
  margin: 0;
  font: 600 var(--card-title-size) / 1 var(--font-display);
}

.craft-tuning summary {
  cursor: pointer;
}

.craft-meta {
  margin: 0;
  font-size: var(--caption-text-size);
  color: var(--muted);
}

.craft-level output {
  font-variant-numeric: tabular-nums;
}

.craft-level input {
  width: 100%;
}

.craft-stock {
  display: grid;
  gap: 6px;
}

.craft-stock-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 36px auto;
  align-items: center;
  gap: 6px;
}

.craft-stock-name {
  display: grid;
}

.craft-stock-count {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.craft-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.craft-deck-note.is-error {
  color: var(--alert);
}

.craft-tuning textarea {
  width: 100%;
  box-sizing: border-box;
  font: 12px / 1.4 ui-monospace, monospace;
  resize: vertical;
}

.craft-log {
  max-height: 180px;
  margin: 0;
  padding-left: 20px;
  overflow-y: auto;
  font-size: var(--caption-text-size);
  color: var(--muted);
}

.craft-log:empty {
  display: none;
}

/* The table: a felt stage the board sits on, and the end over it. */
.craft-table {
  position: relative;
  min-height: 0;
}

.craft-stage {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: var(--panel-radius);
  background: radial-gradient(ellipse 75% 65% at 50% 60%, #3b342b 0%, var(--craft-felt) 55%, #15120f 100%);
  box-shadow: inset 0 0 80px #000b;
  touch-action: none;
}

/* The track holds every card at its row and column; the camera slides it down a row as the settler climbs. */
.craft-track {
  position: absolute;
  left: 50%;
  bottom: var(--gap);
  width: calc(var(--cols) * var(--cw) + (var(--cols) - 1) * var(--gap));
  transform: translateX(-50%);
  translate: 0 calc(var(--cam) * (var(--ch) + var(--gap)));
  transition: translate 120ms ease-out;
}

.craft-card,
.craft-hero {
  position: absolute;
  left: calc(var(--col) * (var(--cw) + var(--gap)));
  bottom: calc(var(--row) * (var(--ch) + var(--gap)));
  width: var(--cw);
  height: var(--ch);
}

/* A card: a frame in its kind's colour round a paper face, the name on top, the art, and what it gives below. One
   class each, so a board card beats the plain button look. */
.craft-card,
.craft-hero,
.craft-info-card,
.craft-end-card {
  box-sizing: border-box;
  padding: calc(var(--cw) * 0.07) calc(var(--cw) * 0.06);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  justify-items: center;
  align-items: center;
  border: calc(var(--cw) * 0.045) solid var(--kind, var(--craft-edge));
  border-radius: calc(var(--cw) * 0.07);
  background: var(--craft-paper);
  color: var(--craft-ink);
  font-family: var(--font-display);
  box-shadow: 0 calc(var(--cw) * 0.035) 0 #0007, 0 calc(var(--cw) * 0.08) calc(var(--cw) * 0.12) #0006;
}

.craft-card {
  cursor: pointer;
}

:where(.craft-card, .craft-info-card, .craft-end-card).is-flaw { --kind: var(--craft-flaw); }
:where(.craft-card, .craft-info-card, .craft-end-card).is-part { --kind: var(--craft-part); }
:where(.craft-card, .craft-info-card, .craft-end-card).is-clamp { --kind: var(--craft-clamp); }
:where(.craft-card, .craft-info-card, .craft-end-card).is-mend { --kind: var(--craft-mend); }
:where(.craft-card, .craft-info-card, .craft-end-card).is-rule { --kind: var(--craft-rule); }
:where(.craft-card, .craft-info-card, .craft-end-card).is-curse { --kind: var(--craft-curse); }
:where(.craft-card, .craft-info-card, .craft-end-card).is-test { --kind: var(--craft-test); }

.craft-name {
  max-width: 100%;
  font-size: calc(var(--cw) * 0.125);
  font-weight: 700;
  font-variant: small-caps;
  line-height: 1;
  text-align: center;
  letter-spacing: 0.02em;
}

.craft-art {
  width: 56%;
  color: var(--kind, var(--craft-ink));
}

.craft-art .i {
  display: block;
  width: 100%;
  height: auto;
}

.craft-foot {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
}

.craft-gives {
  font-size: calc(var(--cw) * 0.1);
  font-weight: 600;
  opacity: 0.75;
}

/* A flaw's damage, bottom right. */
.craft-strike {
  padding: 0 calc(var(--cw) * 0.04);
  border-radius: calc(var(--cw) * 0.03);
  background: var(--craft-hurt);
  color: #fff;
  font-size: calc(var(--cw) * 0.12);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* A card back: dark, a light edge, the gear in the middle. */
.craft-card.is-down {
  grid-template-rows: 1fr;
  border-color: var(--craft-edge);
  background: radial-gradient(circle at 50% 45%, #2b2622, var(--craft-back) 70%);
  cursor: default;
}

.craft-card.is-down .craft-art {
  width: 42%;
  color: var(--craft-edge);
  opacity: 0.55;
}

.craft-card.is-test {
  background: linear-gradient(165deg, #f7e7bd, #e5cf95);
}

/* Taken, empty, behind the settler, or beside them: gone from the table. Only going fades, over 150ms; the rest of
   the row falls away this way before the board slides. */
.craft-card.is-empty,
.craft-card.is-gone {
  opacity: 0;
  scale: 0.85;
  pointer-events: none;
}

.craft-card.is-gone {
  transition: opacity 150ms ease-in, scale 150ms ease-in;
}

/* A choice rings the card, gold to step onto, red to strike; the card the dragged settler is over lifts. A card a
   flaw above strikes down on has a red top edge. */
.craft-card.is-legal {
  --craft-ring: var(--selected-line);
  box-shadow: 0 0 0 calc(var(--cw) * 0.025) var(--craft-ring), 0 calc(var(--cw) * 0.035) 0 #0007, 0 0 calc(var(--cw) * 0.15) var(--craft-ring);
}

.craft-card.is-strike {
  --craft-ring: var(--craft-hurt);
}

.craft-card.is-target {
  z-index: 1;
  scale: 1.06;
  transition: scale 100ms ease-out;
  box-shadow: 0 0 0 calc(var(--cw) * 0.04) var(--craft-ring), var(--craft-lift), 0 0 calc(var(--cw) * 0.3) var(--craft-ring);
}

.craft-card.is-struck {
  border-top-color: var(--craft-hurt);
}

.craft-card.is-look {
  outline: calc(var(--cw) * 0.025) dashed var(--info);
  outline-offset: calc(var(--cw) * 0.03);
}

/* The settler's card. It is dragged onto a card to choose it. The frame's Integrity and clamps are on the blueprint. */
.craft-hero {
  z-index: 2;
  --kind: var(--craft-edge);
  cursor: grab;
  touch-action: none;
}

.craft-hero .craft-art {
  width: 60%;
  color: var(--craft-ink);
}

/* Lifted while dragged: it rides on the stage, placed at the pointer, and lets the pointer through to the card under
   it. */
.craft-hero.is-dragging {
  position: fixed;
  bottom: auto;
  z-index: 10;
  transform: translate(-50%, -50%);
  scale: 1.12;
  pointer-events: none;
  box-shadow: var(--craft-lift);
}

.craft-hero.is-gritty {
  background: radial-gradient(#6b4b2a40 1px, transparent 1.6px) 0 0 / 7px 7px, var(--craft-paper);
}

/* A hit: the settler's card blushes and a slash crosses it. */
.craft-hero.is-hit {
  animation: craft-hit-blush 600ms ease-out;
}

.craft-hero.is-hit::after {
  content: "";
  position: absolute;
  inset: -4%;
  background: linear-gradient(135deg, transparent 45%, var(--craft-hurt) 48%, #fff 50%, var(--craft-hurt) 52%, transparent 55%);
  pointer-events: none;
  animation: craft-hit-slash 300ms ease-out forwards;
}

@keyframes craft-hit-blush {
  from { background: #f3a7a7; }
}

@keyframes craft-hit-slash {
  from { clip-path: inset(0 100% 100% 0); }
  60% { clip-path: inset(0); opacity: 1; }
  to { clip-path: inset(0); opacity: 0; }
}

/* What a hit cost, rising off the settler. It sits on the screen, placed at the settler. */
.craft-float {
  position: fixed;
  z-index: 11;
  color: #fff;
  font: 700 26px / 1 var(--font-display);
  text-shadow: 0 2px 0 var(--craft-hurt), 0 0 10px #000a;
  transform: translate(-50%, -100%);
  pointer-events: none;
  animation: craft-float 1.2s ease-out forwards;
}

@keyframes craft-float {
  from { opacity: 1; translate: 0 0; }
  to { opacity: 0; translate: 0 -34px; }
}

/* Reduced motion: no camera slide, blush, slash, or blueprint glow, and the cost fades where it is. */
@media (prefers-reduced-motion: reduce) {
  .craft-track,
  .craft-card,
  .bp-bar-loss {
    transition: none;
  }

  .bp-spot.is-better,
  .bp-spot.is-hurt,
  .bp-mark.is-saving,
  .bp-bar-fill.is-mended,
  .craft-blueprint.is-approving,
  .craft-blueprint.is-breaking .bp-drawing {
    animation: none;
  }

  .craft-hero.is-hit,
  .craft-hero.is-hit::after {
    animation: none;
  }

  @keyframes craft-float {
    from { opacity: 1; }
    to { opacity: 0; }
  }
}

.craft-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.craft-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 8px;
  border: 1px solid var(--play-line);
  border-radius: var(--control-radius);
  background: var(--surface-1);
  font-family: var(--font-ui);
}

.craft-chip-label {
  font-size: var(--caption-text-size);
  color: var(--muted);
}

.craft-chip-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* The blueprint of the item being made: a line drawing on blueprint paper beside its Integrity, clamps, stats, and
   marks. Changes play on it: a hit shakes the drawing, flashes the flaw's part red, and drains the bar, leaving a red
   chunk that catches up; Integrity falling shows cracks; a part glows gold and stays inked; clamps show on the frame. */
.craft-blueprint {
  --bp-paper: #13396a;
  --bp-line: #d4eaff;
  --bp-faint: #d4eaff59;
  --bp-gold: #ffd36b;
  --bp-good: #7ef0b2;
  --bp-hurt: #ff6b74;
  position: relative;
  display: grid;
  gap: 6px;
  padding: 10px 14px 12px;
  border: 1px solid #5b8fc7;
  border-radius: var(--panel-radius);
  background:
    repeating-linear-gradient(0deg, #ffffff10 0 1px, transparent 1px 14px),
    repeating-linear-gradient(90deg, #ffffff10 0 1px, transparent 1px 14px),
    radial-gradient(ellipse at 30% 20%, #1b4a84, var(--bp-paper) 70%);
  color: var(--bp-line);
  font-family: var(--font-ui);
  box-shadow: inset 0 0 30px #0007, var(--shadow-card);
}

.bp-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font: 600 13px / 1 var(--font-display);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.bp-item {
  color: var(--bp-faint);
  letter-spacing: 0.06em;
}

/* On top of the table the drawing stays small, with the readout and stats beside it; at phone width the stats drop
   under both. Beside the table (from 1200px) everything stacks. */
.bp-body {
  display: grid;
  grid-template-areas: "drawing readout" "drawing stats" "marks marks";
  grid-template-columns: minmax(0, 240px) minmax(0, 1fr);
  align-items: center;
  gap: 8px 14px;
}

.bp-drawing {
  position: relative;
  grid-area: drawing;
}

.bp-svg {
  display: block;
  width: 100%;
  height: auto;
  fill: none;
  stroke: var(--bp-line);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}

.bp-svg .bp-faint {
  stroke: var(--bp-faint);
}

.bp-svg .bp-dim {
  fill: var(--bp-faint);
  stroke: none;
  font: 8px var(--font-ui);
}

.bp-spot {
  transition: stroke 300ms;
}

/* A part fitted stays inked in gold. */
.bp-spot.is-fitted {
  stroke: var(--bp-gold);
}

.bp-spot.is-better {
  animation: bp-better 700ms ease-out;
}

.bp-spot.is-hurt {
  animation: bp-hurt 520ms ease-out;
}

/* A curse stains the frame. */
.bp-spot.is-cursed {
  stroke: #b7d36b;
  stroke-dasharray: 5 2;
}

@keyframes bp-better {
  0%, 30% { stroke: #fff; filter: drop-shadow(0 0 4px var(--bp-gold)) drop-shadow(0 0 8px var(--bp-gold)); }
}

@keyframes bp-hurt {
  0%, 40% { stroke: var(--bp-hurt); filter: drop-shadow(0 0 4px var(--bp-hurt)); }
}

.bp-clamps path,
.bp-cracks path {
  opacity: 0;
  transition: opacity 250ms;
}

.bp-clamps path {
  stroke: var(--bp-gold);
  stroke-width: 2.2;
}

.bp-cracks path {
  stroke: var(--bp-hurt);
  stroke-width: 1.8;
}

.bp-clamps path.is-shown,
.bp-cracks path.is-shown {
  opacity: 1;
}

.bp-readout {
  grid-area: readout;
  display: grid;
  gap: 6px;
  min-width: 0;
}

.bp-integrity {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 2px 8px;
}

.bp-label {
  font-size: 12px;
  color: var(--bp-faint);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.bp-value {
  display: inline-block;
  font: 700 18px / 1 var(--font-display);
  font-variant-numeric: tabular-nums;
}

.bp-bar {
  position: relative;
  grid-column: 1 / -1;
  height: 8px;
  overflow: hidden;
  border: 1px solid var(--bp-faint);
  border-radius: 4px;
  background: #0a2246;
}

.bp-bar-fill,
.bp-bar-loss {
  position: absolute;
  inset: 0 auto 0 0;
}

.bp-bar-fill {
  background: linear-gradient(90deg, #6fd6ff, var(--bp-line));
  transition: width 200ms ease-out;
}

/* The red chunk a hit leaves catches up after a beat. */
.bp-bar-loss {
  background: var(--bp-hurt);
  transition: width 420ms ease-in 260ms;
}

.bp-bar-fill.is-mended {
  animation: bp-mend 700ms ease-out;
}

@keyframes bp-mend {
  0%, 30% { background: var(--bp-good); box-shadow: 0 0 10px var(--bp-good); }
}

.bp-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
}

.bp-clamp-count {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.bp-clamp-count-icon .i {
  display: block;
  width: 18px;
  height: 18px;
  color: var(--bp-gold);
}

.bp-grit {
  padding: 1px 8px;
  border: 1px solid #c9935a;
  border-radius: 10px;
  color: #f0c08a;
  font-size: 12px;
}

.bp-stats {
  grid-area: stats;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 8px;
}

.bp-stat {
  position: relative;
  display: grid;
  gap: 2px;
  padding: 4px 8px;
  border: 1px solid var(--bp-faint);
  border-radius: 6px;
  background: #0d2c5580;
}

.bp-stat .bp-pct {
  color: var(--bp-gold);
  font-size: 20px;
}

.bp-stat-what {
  font-size: 11px;
  color: var(--bp-faint);
}

.bp-marks {
  grid-area: marks;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.bp-marks:empty {
  display: none;
}

.bp-mark {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 8px 1px 4px;
  border: 1px solid var(--bp-gold);
  border-radius: 10px;
  color: var(--bp-gold);
  font-size: 12px;
}

.bp-mark .i {
  width: 14px;
  height: 14px;
}

.bp-mark.is-curse {
  border-color: var(--bp-hurt);
  color: var(--bp-hurt);
}

/* A technique that just held a flaw back pulses. */
.bp-mark.is-saving {
  animation: bp-saving 700ms ease-out;
}

@keyframes bp-saving {
  0%, 35% { scale: 1.2; background: #ffd36b33; box-shadow: 0 0 10px var(--bp-gold); }
}

/* What changed, rising off the number. */
.bp-float {
  position: absolute;
  right: 4px;
  top: -4px;
  color: var(--bp-hurt);
  font: 700 18px / 1 var(--font-display);
  text-shadow: 0 0 6px #000a;
  pointer-events: none;
  animation: craft-float 1.1s ease-out forwards;
}

.bp-float.is-good {
  color: var(--bp-good);
}

.craft-blueprint.is-gritty .bp-drawing {
  background: radial-gradient(#c9935a33 1px, transparent 1.6px) 0 0 / 6px 6px;
}

/* The end: approved glows gold; broken goes grey with every crack. The stamp says which. */
.bp-stamp {
  position: absolute;
  top: 50%;
  left: 50%;
  padding: 2px 10px;
  border: 2px solid currentColor;
  border-radius: 4px;
  font: 700 18px / 1 var(--font-display);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  transform: translate(-50%, -50%) rotate(-12deg);
  pointer-events: none;
}

.bp-stamp:empty {
  display: none;
}

.craft-blueprint.is-approved {
  border-color: var(--bp-gold);
  box-shadow: inset 0 0 30px #0007, 0 0 18px #ffd36b55;
}

.craft-blueprint.is-approved .bp-stamp {
  color: var(--bp-gold);
}

.craft-blueprint.is-broken .bp-svg {
  filter: grayscale(1) brightness(0.8);
}

.craft-blueprint.is-broken .bp-cracks path {
  opacity: 1;
}

.craft-blueprint.is-broken .bp-stamp {
  color: var(--bp-hurt);
}

.craft-blueprint.is-approving {
  animation: bp-approve 900ms ease-out;
}

.craft-blueprint.is-breaking .bp-drawing {
  animation: bp-break 600ms ease-out;
}

@keyframes bp-approve {
  0%, 25% { box-shadow: inset 0 0 30px #0007, 0 0 40px var(--bp-gold); }
}

@keyframes bp-break {
  30% { scale: 1.04; }
}

@media (max-width: 520px) {
  .bp-item {
    display: none;
  }

  .bp-body {
    grid-template-areas: "drawing readout" "stats stats" "marks marks";
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

@media (min-width: 1200px) {
  .bp-body {
    grid-template-areas: "drawing" "readout" "stats" "marks";
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The card a tap reads, large over the screen. */
.craft-info {
  position: fixed;
  inset: 0;
  z-index: 12;
  display: grid;
  place-items: center;
  padding: 16px;
  background: #05090ad0;
  pointer-events: auto;
}

.craft-info-card {
  --cw: min(260px, 70vw);
  width: var(--cw);
  min-height: calc(var(--cw) * 4 / 3);
  gap: 8px;
  align-content: start;
  cursor: default;
  animation: craft-deal 260ms ease-out;
}

.craft-info-card p {
  margin: 0;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.35;
  text-align: center;
}

.craft-info-card .craft-art {
  width: 40%;
}

@keyframes craft-deal {
  from { opacity: 0; transform: perspective(800px) rotateY(70deg) scale(0.85); }
}

/* The end, over the table: the item crafted as a card beside its stats and the XP. */
.craft-end {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  padding: 16px;
  border-radius: var(--panel-radius);
  background: #0b0908d9;
  animation: craft-fade 300ms ease-out;
}

@keyframes craft-fade {
  from { opacity: 0; }
}

.craft-end-panel {
  width: min(560px, 100%);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 20px;
  padding: 20px;
  border: var(--panel-border);
  border-radius: var(--panel-radius);
  background: var(--surface-panel);
  box-shadow: var(--shadow-card);
}

@media (max-width: 520px) {
  .craft-end-panel {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
  }
}

.craft-end-card {
  --cw: 150px;
  --kind: var(--craft-test);
  position: relative;
  width: var(--cw);
  height: calc(var(--cw) * 4 / 3);
  animation: craft-deal 420ms ease-out 120ms backwards;
}

/* A cracked frame: grey, with a crack across it. */
.craft-end-card.is-cracked {
  --kind: var(--craft-edge);
  filter: grayscale(0.8);
}

.craft-end-card.is-cracked::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 47%, var(--craft-ink) 48%, transparent 50%);
  clip-path: polygon(0 30%, 40% 45%, 55% 40%, 100% 70%, 100% 74%, 55% 44%, 40% 49%, 0 34%);
}

.craft-end-facts {
  display: grid;
  gap: 8px;
  min-width: 0;
}

.craft-end-facts p {
  margin: 0;
}

.craft-end-title {
  margin: 0;
  font: 700 26px / 1 var(--font-display);
  color: var(--gold-200);
}

.craft-end-title.is-cracked {
  color: var(--alert);
}

.craft-end-stats,
.craft-end-xp {
  display: grid;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.craft-end-stats li {
  display: grid;
}

.craft-end-stat {
  font: 700 20px / 1.1 var(--font-display);
}

/* Elevation playground (/#elevation): the fold button stands in the top left
   corner and the panel opens below it. */
.elev-fold {
  position: absolute;
  top: 12px;
  left: 12px;
  width: 36px;
  height: 36px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 8px;
  box-shadow: var(--shadow);
}

.elev-fold .i {
  width: 20px;
  height: 20px;
}

.walker-playground:has(.elev-fold) > .walker-panel {
  top: 56px;
  max-height: calc(100% - 68px);
}

.elev-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.elev-wrap button[aria-pressed="true"] {
  background: var(--accent);
}

.elev-stats {
  white-space: pre-line;
  font-variant-numeric: tabular-nums;
}

/* Kitbash playground (/#kitbash): the main panel and the spec panel stack
   down the left; the spec panel scrolls. */
.kit-side {
  position: absolute;
  top: 12px;
  left: 12px;
  bottom: 12px;
  width: 260px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}

.kit-right {
  left: auto;
  right: 12px;
}

.kit-lines {
  white-space: pre-line;
}

.kit-side > .walker-panel {
  position: static;
  width: auto;
  max-height: none;
  pointer-events: auto;
}

.kit-side > .walker-panel:not(.kit-spec) {
  flex: none;
}

.kit-spec {
  min-height: 0;
  overflow-y: auto;
}

.kit-spec details {
  display: grid;
  gap: 6px;
}

.kit-spec details[open] {
  padding-bottom: 6px;
}

.kit-spec summary {
  cursor: pointer;
  font-weight: 600;
}

.kit-spec .walker-row select {
  flex: 1;
  min-width: 0;
}

.kit-places,
.kit-gear {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.kit-gear .walker-row {
  flex: 1;
}

.kit-share .kit-code {
  display: grid;
  gap: 4px;
}

.kit-share textarea {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  font: 11px/1.3 ui-monospace, monospace;
  word-break: break-all;
}

.kit-share input[type="text"],
.kit-share select {
  flex: 1;
  min-width: 0;
  font-size: inherit;
}

.screen-play .member-new {
  padding: 1px 7px;
  border-radius: 5px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
}

.screen-play .family-count {
  margin: 0 0 8px;
  font-weight: 600;
  color: var(--text-dim);
}

.dialogue-sprite {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  animation: dialogue-sprite var(--sprite-ms) steps(var(--sprite-frames)) infinite;
}

@keyframes dialogue-sprite {
  from {
    background-position-x: var(--sprite-x0);
  }
  to {
    background-position-x: var(--sprite-x1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .dialogue-sprite {
    animation: none;
    background-position-x: var(--sprite-x0);
  }
}

/* A clanker's pixel face runs wide, so its dialogue is set smaller. */
.dialogue-bubble.is-robot {
  --dialogue-emphasis: #8fe3ff;
}

.dialogue-bubble.is-robot :is(.dialogue-name, .dialogue-line) {
  font-family: var(--font-pixel);
  font-size: 12px;
  line-height: 1.7;
}

.wipe-cursor {
  position: relative;
}

.wipe-cursor::after {
  content: "";
  position: absolute;
  left: calc(100% + 0.5em);
  top: 0;
  width: 0.55em;
  height: 1em;
  background: currentColor;
}

/* Seats: who works a building (city/seats.js). Round settler seats show a face, ringed gold
   for the founders; square clanker seats show a clanker's head; an empty seat is dashed, ringed
   red while nobody is in charge. The card and Manage draw the same seats. */
.seat-strip {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}

.garrison-seats {
  display: inline-grid;
  grid-template-columns: repeat(5, max-content);
}

.seat {
  appearance: none;
  flex: none;
  display: grid;
  place-items: center;
  width: var(--seat);
  height: var(--seat);
  padding: 0;
  overflow: hidden;
  background: #0e171c;
  border: 1.5px dashed var(--play-line-strong);
  color: var(--muted);
  font: 600 14px/1 var(--font-ui);
}

button.seat {
  cursor: pointer;
}

.seat-settler {
  --seat: 32px;
  border-radius: 50%;
}

.seat-clanker,
.seat-troop {
  --seat: 24px;
  border-radius: 4px;
}

.seat.is-filled {
  border-style: solid;
  border-color: #0b1216;
}

.seat-settler.is-filled {
  background: #f6f1e5;
}

.seat-settler.is-founding {
  border: 2px solid var(--gold-400);
}

.seat-clanker.is-filled,
.seat-troop.is-filled {
  background: #3a4a55;
}

.seat.is-need {
  border: 2px solid var(--alert);
  box-shadow: 0 0 8px #ff858c66;
}

.seat canvas,
.tray-pic canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.seat-bust {
  width: 100%;
  height: 100%;
  background-repeat: no-repeat;
  image-rendering: pixelated;
}

.seat.is-pop {
  animation: seat-pop 0.28s cubic-bezier(0.3, 1.6, 0.5, 1);
}

.seat.is-emptied {
  animation: seat-emptied 0.24s ease-out;
}

@keyframes seat-pop {
  0% { transform: scale(0.55); }
  60% { transform: scale(1.14); }
  100% { transform: scale(1); }
}

@keyframes seat-emptied {
  0% { transform: scale(1.12); opacity: 0.4; }
  100% { transform: scale(1); opacity: 1; }
}

/* The output figure counts to a new value and flashes the way it moved. */
.pct {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  padding: 0 4px;
  border-radius: 4px;
  font-family: var(--font-ui);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  transition: color 1s, background-color 1s;
}

.pct .pct-arrow {
  font-size: 0.8em;
  opacity: 0;
  transition: opacity 1s;
}

.pct.is-up {
  color: #9ff0c1;
  background: #3dbe7833;
  transition: none;
}

.pct.is-down {
  color: #ffc2c6;
  background: #ff858c33;
  transition: none;
}

.pct.is-up .pct-arrow,
.pct.is-down .pct-arrow {
  opacity: 1;
  transition: none;
}

.screen-play .city-sheet-seats {
  display: contents;
}

.city-sheet-crew-slots { grid-column: 2 / -1; }

/* A gathering hut's walk and pace sit under its crew: label, then the words
   across the rest of the row. */
.screen-play :is(.city-sheet-gather, .city-sheet-stat) {
  display: contents;
}

.city-sheet-stat > .walk-tone { grid-column: 2 / -1; }

.screen-play .city-sheet-gather[hidden] {
  display: none;
}

.screen-play .city-sheet-seats[hidden] {
  display: none;
}

/* Manage: bigger seats, a + in an empty settler seat, and the hint beside them. Clanker seats
   only show who works there: the server staffs them. */
#manage-panel .seat-settler {
  --seat: 44px;
}

#manage-panel .seat-clanker {
  --seat: 34px;
}

#manage-panel .seat-settler:not(.is-filled)::after {
  content: "+";
}

.upgrade-table tr.is-line td {
  vertical-align: middle;
}

.manage-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

#manage-panel .manage-line .pct {
  font-size: 18px;
}

.output-why,
.seat-hint.is-alert,
.need-line.is-off {
  color: var(--alert);
}

.output-why {
  font-weight: 600;
}

.output-idle {
  color: var(--muted);
}

.seat-hint {
  font-size: var(--meta-text-size);
  color: var(--muted);
}

/* A gatherer hut's walk and pace (gather.js walkTone): green near, amber, red far. */
.walk-tone {
  font-weight: 600;
}

.walk-tone[data-tone="ok"] {
  color: var(--ok);
}

.walk-tone[data-tone="warn"] {
  color: var(--warn);
}

.walk-tone[data-tone="alert"] {
  color: var(--alert);
}

/* Standing work's Off switch sits at the end of its output row. */
.manage-off {
  margin-left: auto;
}

/* A Needs row's needs (energy, water), each a round mark, green ✓ or red ✕, and its words. */
.need {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.need + .need {
  margin-left: 16px;
}

.need-mark {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-weight: 700;
  font-size: 14px;
}

.need-mark.is-on {
  background: var(--ok);
  color: var(--ok-ink);
}

.need-mark.is-off {
  background: var(--alert);
  color: var(--alert-ink);
}

/* The tray of faces that replaces the Manage rows to choose who helps. */
.tray-title {
  margin-bottom: 10px;
  font: 700 20px/1.1 var(--font-display);
}

.tray-faces {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
}

.tray-face {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 3px;
  padding: 8px 6px;
  border: 1px solid var(--play-line);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font-ui);
  text-align: center;
}

button.tray-face {
  cursor: pointer;
}

button.tray-face:hover {
  border-color: var(--play-line-strong);
  background: var(--surface-3);
}

.tray-pic {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  overflow: hidden;
  border: 1px solid #000;
  border-radius: 50%;
  background: #f6f1e5;
}

.tray-face.is-founding .tray-pic {
  border: 3px solid var(--gold-400);
}

.tray-face.is-none .tray-pic {
  border: 2px dashed var(--play-line-strong);
  background: #0e171c;
  color: var(--muted);
}

.tray-face.is-none .tray-pic::after {
  content: "✕";
  font-size: 22px;
}

.tray-face.is-away {
  opacity: 0.45;
}

.tray-name {
  font-weight: 600;
  font-size: 15px;
}

.tray-where {
  font-size: 12px;
  color: var(--gold-200);
}

.tray-gain {
  font-size: 13px;
  color: var(--ok-text);
}

/* The floor plan section of Manage (city/floor_plan.js) is the Fixtures card, below the rows'
   own card (an inventory box each). The board holds the grid on the left and the detail on the
   right; when the pane is too narrow for both, the detail wraps under the grid. The tray runs
   full width below. One square per plan cell, --fp-cell at most,
   smaller when the plan is wider than the pane. Open floor is a panel well, floor a later level
   opens is dashed with that level, and void is nothing. A fixture's cells sit on top in its
   rarity's colours, joined into one shape; the picked one is lit as anything selected, one
   placed but not yet installed is lit green, a proposed build not yet paid for is lit green and outlined dashed, the one being
   dragged dims, and where a drag would drop shows green, or red where it can't. */
/* A closed floor (floor_plan.js paintClosed): the whole section dims and blurs under a veil
   that takes every tap, its pill in the middle. */
.fp-panel {
  position: relative;
}

/* The plan's sections, Installed and the tray's, set apart by more than a
   section's own title and tiles. */
.screen-play .fp-panel,
.fp-tray {
  gap: 16px;
}

.fp-tray {
  display: flex;
  flex-direction: column;
}

.fp-closed {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  border-radius: inherit;
  background: color-mix(in srgb, #000 45%, transparent);
  backdrop-filter: blur(3px);
}

.fp-board {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 16px;
}

.fp-plan {
  max-width: 100%;
}

/* A turret's Installed card (floor_plan.js slotPart), a tray tile on its own. */
.fp-slot {
  width: 200px;
}

.fp-detail {
  flex: 1 1 220px;
  min-width: 0;
}

.fp-moves {
  display: flex;
  gap: 8px;
  margin-bottom: 14px;
}

.fp-grid {
  --fp-cell: 40px;
  display: grid;
  grid-template-columns: repeat(var(--fp-cols), minmax(0, 1fr));
  grid-template-rows: repeat(var(--fp-rows), minmax(0, 1fr));
  width: calc(var(--fp-cols) * var(--fp-cell));
  max-width: 100%;
  aspect-ratio: var(--fp-cols) / var(--fp-rows);
}

.fp-layer {
  display: contents;
}

.fp-cell,
.fp-piece,
.fp-ghost {
  margin: 1px;
  border-radius: var(--control-radius);
}

.fp-cell {
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: var(--caption-text-size);
}

.fp-cell.is-open {
  border: 1px solid var(--play-line);
  background: var(--surface-1);
}

.fp-cell.is-locked {
  border: 1px dashed var(--play-line);
  background: var(--surface-0);
}

/* A fixture that drags (a tray tile, a piece on the grid): a mouse drag over it selects
   nothing. A finger that starts sideways on a tray tile drags it; one that starts up or down
   scrolls the pane, so the tray at the foot of Manage still scrolls. */
[data-drag] {
  touch-action: pan-y;
  user-select: none;
  cursor: grab;
}

/* A tray tile's picture is its fixture's footprint, the blue piece in miniature: cells on a
   square of the larger side, at least two cells across. */
.screen-play .inv-tile > .fp-glyph {
  --fp-glyph-cell: calc((36px - (var(--fp-glyph-side) - 1) * 2px) / var(--fp-glyph-side));
  grid-row: 1 / span 2;
  display: grid;
  grid-template-columns: repeat(var(--fp-glyph-cols), var(--fp-glyph-cell));
  grid-template-rows: repeat(var(--fp-glyph-rows), var(--fp-glyph-cell));
  place-content: center;
  gap: 2px;
  width: 36px;
  height: 36px;
}

/* A rare or epic tile keeps the plain card: its footprint's squares wear the tier's colours
   (the palette its is-rare or is-epic sets), and a common one's stay blue. */
.fp-glyph-cell {
  border: 1px solid var(--tier-deep, var(--info-deep));
  border-radius: 2px;
  background: var(--tier-mid, color-mix(in srgb, var(--info-deep) 35%, var(--surface-1)));
}

/* Every piece is blue whatever its rarity, so only the selected one wears gold. */
.fp-piece {
  display: grid;
  place-items: center;
  border: 1px solid var(--info-deep);
  background: color-mix(in srgb, var(--info-deep) 35%, var(--surface-1));
  color: var(--info);
}

/* A finger on a piece that drags moves it in any direction, never the pane. A piece on a
   locked plan has no data-drag, so a finger on it scrolls. */
.fp-piece[data-drag] {
  touch-action: none;
}

.fp-piece[data-join~="t"] {
  margin-top: 0;
  border-top-width: 0;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

.fp-piece[data-join~="r"] {
  margin-right: 0;
  border-right-width: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.fp-piece[data-join~="b"] {
  margin-bottom: 0;
  border-bottom-width: 0;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.fp-piece[data-join~="l"] {
  margin-left: 0;
  border-left-width: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.fp-piece > .i {
  width: 70%;
  height: 70%;
}

.fp-piece.is-picked {
  border-color: var(--selected-line);
  background: var(--selected-fill);
  color: var(--selected-ink);
  box-shadow: var(--selected-glow);
}

.fp-piece.is-proposed {
  border-style: dashed;
}

.fp-piece.is-new,
.fp-piece.is-proposed {
  border-color: var(--ok);
  background: var(--btn-go-fill);
  color: var(--ok-text);
}

.fp-piece.is-new.is-picked {
  box-shadow: inset 0 0 9px color-mix(in srgb, var(--ok) 25%, transparent);
}

.fp-piece.is-lifted {
  opacity: var(--dim-opacity);
}

.fp-ghost {
  background: color-mix(in srgb, var(--ok-text) 40%, transparent);
  pointer-events: none;
}

.fp-ghost.is-bad {
  background: color-mix(in srgb, var(--alert) 45%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .seat.is-pop,
  .seat.is-emptied {
    animation: none;
  }
}

/* Crest editor (src/crest/designer.js), full screen over the play screen:
   Cancel and Save head it. The charge list runs beside the crest, the edit
   band (palette, picker, sliders) sits under both, and the catalogue
   fills the rest, all as wide as the play screen's floating panels at most
   and centred, on every screen. */
.crest-designer {
  position: fixed;
  inset: 0;
  z-index: var(--z-editor);
  display: grid;
  /* The charge list is one row wide whatever the screen; the crest takes
     the rest. */
  grid-template-columns: 124px minmax(0, 1fr);
  grid-template-rows: auto auto auto minmax(0, 1fr);
  grid-template-areas: "head head" "layers main" "edit edit" "catalogue catalogue";
  gap: 8px;
  padding: 0 max(12px, calc((100% - var(--float-width)) / 2)) 12px;
  background: var(--bg);
  color: var(--ink);
  font-size: 14px;
  pointer-events: auto;
  /* A charge row is as tall as the two flip buttons stacked beside it. */
  --crest-tools-gap: 6px;
  --crest-row-height: calc(2 * var(--icon-control-size) + var(--crest-tools-gap));
}

.crest-head {
  grid-area: head;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 4px 8px;
  padding-top: max(12px, env(safe-area-inset-top));
}

.crest-status {
  flex-basis: 100%;
  text-align: center;
}

.crest-main {
  grid-area: main;
  display: flex;
  justify-content: center;
  align-items: start;
}

.crest-head-end {
  display: flex;
  gap: 6px;
}

.crest-stage {
  position: relative;
  width: min(100%, 42dvh);
  aspect-ratio: 1;
}

.crest-random {
  position: absolute;
  right: 0;
  bottom: 0;
}

.crest-art {
  display: block;
  width: 100%;
  height: 100%;
}

/* The cursor says what a press will do: a pointing finger picks another
   charge, an open hand drags the selected one, a closed hand is dragging. */
.crest-live {
  touch-action: none;
}

.crest-live [data-charge] {
  cursor: pointer;
}

.crest-live [data-selected] {
  cursor: grab;
}

.crest-live.crest-dragging,
.crest-live.crest-dragging * {
  cursor: grabbing;
}

.crest-edit {
  grid-area: edit;
  display: grid;
  gap: 8px;
}

.crest-palette {
  display: flex;
  gap: 8px;
}

.crest-swatch {
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid var(--line);
  display: grid;
  place-items: center;
}

.crest-swatch[aria-pressed="true"] {
  border-color: var(--ink);
  box-shadow: 0 0 0 2px var(--accent);
}

.crest-picker-host[hidden] {
  display: none;
}

/* Hue runs the full width; saturation and brightness share the row under
   it. */
.crest-picker {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 12px;
  padding: 10px;
  border-radius: 8px;
  background: var(--panel);
}

.crest-slider {
  display: grid;
  grid-template-columns: 76px 1fr;
  align-items: center;
  gap: 8px;
}

.crest-picker input[type="range"],
.crest-slider input[type="range"] {
  --thumb: 26px;
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: var(--thumb);
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}

.crest-picker input[type="range"]::-webkit-slider-runnable-track,
.crest-slider input[type="range"]::-webkit-slider-runnable-track {
  height: 14px;
  border-radius: 99px;
  background: var(--track-bg, var(--line));
}

.crest-picker input[type="range"]::-moz-range-track,
.crest-slider input[type="range"]::-moz-range-track {
  height: 14px;
  border-radius: 99px;
  background: var(--track-bg, var(--line));
}

.crest-picker input[type="range"]::-webkit-slider-thumb,
.crest-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: var(--thumb);
  height: var(--thumb);
  margin-top: calc((14px - var(--thumb)) / 2);
  border-radius: 50%;
  border: 3px solid #fff;
  background: var(--ink);
  box-shadow: 0 1px 4px #0006;
}

.crest-picker input[type="range"]::-moz-range-thumb,
.crest-slider input[type="range"]::-moz-range-thumb {
  width: var(--thumb);
  height: var(--thumb);
  border-radius: 50%;
  border: 3px solid #fff;
  background: var(--ink);
  box-shadow: 0 1px 4px #0006;
}

.crest-strip-h {
  grid-column: 1 / -1;
  --track-bg: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
}

.crest-strip-s {
  --track-bg: linear-gradient(to right, var(--sat-from), var(--sat-to));
}

.crest-strip-v {
  --track-bg: linear-gradient(to right, #000, var(--val-to));
}

.crest-tabs {
  display: flex;
  gap: 4px;
}

.crest-tabs button {
  flex: 1;
  padding: 7px 8px;
  border-radius: 6px;
  background: var(--line);
  color: var(--ink);
}

.crest-tabs button[aria-selected="true"] {
  background: var(--accent);
  color: var(--on-accent);
}

/* The tabs stay put; only the grid under them scrolls. */
.crest-catalogue {
  grid-area: catalogue;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.crest-grid {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  align-content: start;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 6px;
}

.crest-tile {
  aspect-ratio: 1;
  padding: 6px;
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink);
  border: 2px solid transparent;
}

.crest-tile[aria-pressed="true"] {
  border-color: var(--ink);
}

.crest-charge-art {
  display: block;
  width: 100%;
  height: 100%;
}

/* The crest alone sets its row's height: the list takes no size from its
   rows, stretches to the crest beside it, and scrolls the rest. */
.crest-layers {
  grid-area: layers;
  display: grid;
  align-content: start;
  min-height: 0;
  overflow-y: auto;
  contain: size;
}

.crest-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
}

/* In the narrow list a row is the charge between its four corner
   buttons: delete top left, its colour top right, up and down along the
   bottom. */
.crest-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "remove pick colour" "up pick down";
  align-items: center;
  min-height: var(--crest-row-height);
  border-radius: 8px;
  background: var(--panel);
  border: 2px solid transparent;
}

.crest-row[aria-selected="true"] {
  border-color: var(--ink);
}

.crest-row button {
  background: transparent;
  color: var(--ink);
  padding: 4px 6px;
}

.crest-row-pick {
  grid-area: pick;
  display: flex;
  justify-content: center;
  align-items: center;
}

.crest-row [data-act="up"] {
  grid-area: up;
}

.crest-row [data-act="down"] {
  grid-area: down;
}

.crest-row [data-act="remove"] {
  grid-area: remove;
}

.crest-row button.crest-row-colour {
  grid-area: colour;
  justify-self: center;
  width: 20px;
  height: 20px;
  margin: 4px;
  padding: 0;
  border-radius: 4px;
  border: 1px solid var(--line);
}

.crest-row-art {
  width: 44px;
  height: 44px;
  flex: none;
}

/* A charge's colour choice: the palette beside its row, fixed to the screen
   so the list's scrolling does not clip it. */
.crest-slot-pop {
  position: fixed;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(3, 44px);
  gap: 6px;
  padding: 6px;
  border-radius: 8px;
  background: var(--panel);
  box-shadow: var(--shadow);
}

.crest-slot-pop[hidden] {
  display: none;
}

/* The selected charge's flips, beside its row under any open palette. */
.crest-row-tools {
  position: fixed;
  z-index: 1;
  display: grid;
  gap: var(--crest-tools-gap);
}

.crest-row-tools[hidden] {
  display: none;
}

.crest-slot {
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 8px;
  border: 2px solid var(--line);
}

.crest-slot[aria-pressed="true"] {
  border-color: var(--ink);
  box-shadow: 0 0 0 2px var(--accent);
}

.crest-empty {
  color: var(--muted);
  margin: 8px 0;
}

.crest-controls {
  display: grid;
  gap: 8px;
}

/* No turn sits in the middle of the turn slider; a mark shows where. */
.crest-slider-turn input {
  --track-bg: linear-gradient(to right, var(--line) calc(50% - 1px), var(--muted) 0 calc(50% + 1px), var(--line) 0);
}

/* Generator study playground (/#generator-study) */
.generator-study {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  flex-direction: column;
  background: #c9ccd0;
  color: #20282c;
  font: 14px system-ui, sans-serif;
}
.generator-study-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px 18px;
  background: #f3f3f0;
  border-bottom: 1px solid #a7aaac;
}
.generator-study-toolbar strong { font-size: 18px; }
.generator-study-toolbar [data-status] {
  display: inline-block;
  margin-left: 10px;
  font-size: 13px;
  font-weight: 400;
  color: #526268;
}
.generator-study-controls, .generator-study-controls label {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
}
.generator-study-controls label { font-size: inherit; }
.generator-study-controls { flex-wrap: wrap; }
.generator-study-controls input { accent-color: #916b20; margin: 0; padding: 0; }
.generator-study-controls button, .generator-study-controls a {
  background: #fff;
  color: #253235;
  border: 1px solid #a4adae;
  border-radius: 4px;
  padding: 7px 10px;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}
.generator-study :focus-visible { outline: 3px solid #916b20; outline-offset: 2px; }
.generator-study-view { position: relative; flex: 1; min-height: 0; }
.generator-study-view canvas { display: block; width: 100%; height: 100%; touch-action: none; }
.generator-study-hint {
  position: absolute;
  bottom: 12px;
  left: 14px;
  padding: 6px 9px;
  border: 1px solid #afb3b6;
  border-radius: 4px;
  background: #eef0f0d9;
  font-size: 12px;
  pointer-events: none;
}
@media (max-width: 560px) {
  .generator-study-toolbar { padding: 10px 12px; }
  .generator-study-controls { font-size: 12px; }
}

/* Standalone 3D character study. */
.character-study {
  --study-panel: #f3f1e9;
  --study-ink: #273d36;
  --study-muted: #727d74;
  --study-line: #d9ddd1;
  --study-accent: #3e6250;
  --accent: var(--study-accent);
  --bg: var(--study-panel);
  --on-accent: #fff;
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  grid-template: 96px minmax(0, 1fr) 34px / 340px minmax(0, 1fr);
  color: var(--study-ink);
  background: var(--study-panel);
  font:
    14px/1.4 system-ui,
    sans-serif;
}
.character-study button {
  background: transparent;
  color: inherit;
  border: 1px solid var(--study-line);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 550;
  padding: 9px 13px;
}
.character-study button:hover {
  background: #e3e8dc;
}
.character-study button:focus-visible {
  outline: 2px solid var(--study-accent);
  outline-offset: 3px;
}
.character-study button[aria-pressed="true"] {
  background: #dfe8d8;
  border-color: var(--study-accent);
  color: #294433;
}
.character-study h1,
.character-study h2,
.character-study p {
  margin: 0;
}
.character-study-header {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 19px 28px;
  border-bottom: 1px solid var(--study-line);
  gap: 16px;
}
.character-study-eyebrow {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.17em;
  color: var(--study-muted);
}
.character-study h1 {
  font:
    28px/1.3 Georgia,
    serif;
  letter-spacing: -0.7px;
}
.character-study h1 span {
  font:
    11px system-ui,
    sans-serif;
  vertical-align: top;
  margin: 4px 0 0 10px;
  display: inline-block;
  color: var(--study-muted);
  letter-spacing: 0;
}
.character-study-actions {
  display: flex;
  gap: 8px;
}
.character-study-actions [data-action="shuffle"] {
  background: var(--study-accent);
  color: #fff;
  border-color: var(--study-accent);
}
.character-study-editor {
  min-height: 0;
  overflow: auto;
  scrollbar-width: thin;
  border-right: 1px solid var(--study-line);
  padding: 24px 22px 16px;
}
.character-study-intro h2 {
  font:
    21px Georgia,
    serif;
}
.character-study-intro p {
  margin-top: 6px;
  color: var(--study-muted);
  font-size: 12px;
}
.character-study-examples {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 7px;
  margin: 20px 0 22px;
}
.character-study-examples button {
  padding: 0;
  overflow: hidden;
  border-radius: 6px;
  font-size: 10px;
}
.character-study-examples img {
  width: 100%;
  height: 72px;
  display: block;
  object-fit: cover;
}
.character-study-examples span {
  display: block;
  padding: 5px 2px;
}
.character-study-tabs {
  display: flex;
  border-bottom: 1px solid var(--study-line);
  margin-bottom: 22px;
  padding-bottom: 8px;
  gap: 4px;
}
.character-study-tabs button {
  flex: 1;
  padding: 8px 3px;
  border-color: transparent;
}
.character-study-options fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 22px;
  min-width: 0;
}
.character-study-options legend {
  padding: 0;
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 650;
}
.character-study-parts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 7px;
}
.character-study-parts button {
  padding: 5px 1px 8px;
  min-width: 0;
  font-size: 10px;
  font-weight: 450;
}
.character-study-parts svg {
  width: 100%;
  height: 48px;
  display: block;
}
.character-study-parts span {
  display: block;
  white-space: nowrap;
}
.character-study-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  padding: 2px;
}
.character-study-swatches button {
  width: 25px;
  height: 25px;
  border: 0;
  padding: 0;
  background: var(--swatch);
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px #00000012;
}
.character-study-swatches button:hover {
  background: var(--swatch);
  transform: scale(1.12);
}
.character-study-swatches button[aria-pressed="true"] {
  background: var(--swatch);
  outline: 1px solid var(--study-accent);
  outline-offset: 3px;
}
.character-study-slider {
  display: block;
  margin: 28px 0;
  font-size: 12px;
}
.character-study-slider > span {
  display: flex;
  justify-content: space-between;
}
.character-study-slider output {
  color: var(--study-muted);
}
.character-study-slider input {
  width: 100%;
  margin: 16px 0 10px;
  accent-color: var(--study-accent);
}
.character-study-range-labels {
  color: var(--study-muted);
  font-size: 10px;
}
.character-study-editor-foot {
  border-top: 1px solid var(--study-line);
  color: var(--study-muted);
  padding-top: 17px;
  margin-top: 12px;
  font-size: 10px;
}
.character-study-main {
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr) 201px;
}
.character-study-view {
  position: relative;
  min-height: 0;
  overflow: hidden;
  background: #deddd3;
}
.character-study-view canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  outline: 0;
}
.character-study-viewbar {
  position: absolute;
  inset: 20px 24px auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  pointer-events: none;
}
.character-study-viewbar > span {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: #f3f1e9cc;
  padding: 5px 9px;
  border-radius: 5px;
}
.character-study-segment {
  display: flex;
  padding: 3px;
  background: #f3f1e9df;
  border: 1px solid #ffffff50;
  border-radius: 8px;
  pointer-events: auto;
  backdrop-filter: blur(12px);
}
.character-study-segment button {
  padding: 6px 11px;
  border-color: transparent;
  font-size: 11px;
}
.character-study-segment button[aria-pressed="true"] {
  background: var(--study-panel);
  border-color: #c3cabb;
  box-shadow: 0 1px 3px #00000008;
}
.character-study-orbit {
  position: absolute;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}
.character-study-orbit > span {
  font-size: 10px;
  color: #49554f;
  background: #f3f1e9cc;
  border-radius: 3px;
  padding: 2px 8px;
}
.character-study-render {
  position: absolute;
  bottom: 22px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-radius: 5px;
  background: #f3f1e9dd;
  padding: 8px 10px;
}
.character-study-render label {
  flex-direction: row;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  cursor: pointer;
}
.character-study-render input {
  accent-color: var(--study-accent);
  margin: 0;
  padding: 0;
}
.character-study-comparison {
  border-top: 1px solid var(--study-line);
  padding: 15px 24px;
}
.character-study-comparison-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  gap: 12px;
}
.character-study-comparison h2 {
  font:
    17px Georgia,
    serif;
}
.character-study-comparison h2 span {
  font:
    10px system-ui,
    sans-serif;
  color: var(--study-muted);
  margin-left: 10px;
}
.character-study-comparison p {
  font-size: 10px;
  color: var(--study-muted);
  margin-top: 3px;
}
.character-study-saved {
  display: grid;
  grid-template-columns: repeat(var(--study-slots), minmax(0, 1fr));
  gap: 12px;
}
.character-study-card {
  position: relative;
  height: 99px;
  border: 1px solid var(--study-line);
  border-radius: 6px;
  overflow: hidden;
}
.character-study-card > button:first-child {
  display: flex;
  align-items: end;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
  padding: 0;
  text-align: left;
}
.character-study-card img {
  width: 83px;
  height: 100%;
  object-fit: cover;
}
.character-study-card > button:first-child span {
  padding: 10px;
  font-size: 10px;
}
.character-study-card.is-empty {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-style: dashed;
  color: #a7afa0;
}
.character-study-card.is-empty > span {
  font:
    20px Georgia,
    serif;
}
.character-study-card.is-empty small {
  font-size: 9px;
  margin-top: 5px;
}
.character-study-card .character-study-remove {
  position: absolute;
  top: 3px;
  right: 3px;
  padding: 0;
  width: 21px;
  height: 21px;
  border: 0;
  background: #f3f1e9dd;
  font-size: 15px;
}
.character-study-footer {
  grid-column: 1 / -1;
  border-top: 1px solid var(--study-line);
  padding: 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--study-muted);
  font-size: 10px;
  gap: 16px;
}
@media (max-width: 1000px) {
  .character-study {
    grid-template-columns: 290px minmax(0, 1fr);
  }
  .character-study-editor {
    padding: 20px 16px;
  }
  .character-study-card > button:first-child span {
    display: none;
  }
  .character-study-card img {
    width: 100%;
  }
  .character-study-render {
    right: 10px;
    bottom: 18px;
  }
  .character-study-orbit {
    left: 15px;
    transform: none;
  }
  .character-study-viewbar {
    inset: 15px 15px auto;
  }
  .character-study-viewbar > span {
    display: none;
  }
}
@media (max-width: 650px) {
  .character-study {
    overflow-y: auto;
    grid-template: max-content 430px max-content 201px max-content / minmax(0, 1fr);
  }
  .character-study-header {
    grid-column: 1;
    padding: 14px 16px;
    align-items: start;
    flex-direction: column;
    gap: 12px;
  }
  .character-study h1 {
    font-size: 24px;
  }
  .character-study-actions {
    width: 100%;
  }
  .character-study-actions button {
    flex: 1;
    padding: 8px 6px;
  }
  .character-study-main {
    display: contents;
  }
  .character-study-view {
    height: 430px;
    grid-row: 2;
  }
  .character-study-comparison {
    grid-row: 4;
  }
  .character-study-editor {
    grid-row: 3;
    overflow: visible;
    border: 0;
    padding: 22px 18px;
  }
  .character-study-examples {
    margin: 14px 0;
  }
  .character-study-examples img {
    height: 85px;
    object-fit: contain;
    background: #deddd3;
  }
  .character-study-parts {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  .character-study-footer {
    grid-column: 1;
    grid-row: 5;
    padding: 12px 18px;
    align-items: start;
  }
  .character-study-footer [data-stats] {
    white-space: nowrap;
  }
}

#garrison-panel > label { display: block; margin-block: .75rem; }
#garrison-types { margin-block: .75rem; }
#garrison-types .tray-pic { font-size: 32px; }
.garrison-portrait { width: 56px; height: 56px; image-rendering: pixelated; }
.garrison-summary { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-block: 10px; }
.garrison-summary[hidden], .garrison-summary [hidden] { display: none; }
.garrison-fill { flex: 1; min-width: 140px; border-radius: 5px; overflow: hidden; }
.garrison-fill .chip-qty { justify-content: center; padding-inline: 8px; }
.garrison-fill.is-short .chip-qty, .garrison-shortage { color: var(--danger); }
.garrison-shortage { flex-basis: 100%; margin: 0; font-size: 13px; }

/* UI playground: only the fixture's layout lives here; its controls use the game styles above. */
.ui-playground {
  --ui-playground-menu-space: 78px;
  position: fixed;
  inset: 0;
  z-index: 1000;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: #0c1416;
}

.ui-playground-toolbar {
  display: flex;
  align-items: center;
  gap: 12px 20px;
  flex-wrap: wrap;
  padding: 8px 16px;
  background: #252a2b;
  color: #fff;
  font: 14px sans-serif;
}

.ui-playground-toolbar strong { flex: none; }
.ui-playground-contexts {
  display: flex;
  flex: 1 1 240px;
  gap: 6px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: thin;
}
.ui-playground-contexts button {
  flex: none;
  white-space: nowrap;
  padding: 7px 10px;
  border: 1px solid #657174;
  border-radius: 3px;
  background: transparent;
  color: #e4e9e9;
  font: inherit;
  cursor: pointer;
}
.ui-playground-contexts button:hover { background: #384346; }
.ui-playground-contexts button[aria-pressed="true"] { background: #e2ebeb; color: #182224; border-color: #e2ebeb; }
.ui-playground-contexts button:focus-visible { outline: 2px solid #c5e6ee; outline-offset: 2px; }
.ui-playground-screen { flex: 1; height: auto; overflow: hidden; display: flex; }
.ui-playground-radio {
  --ui-playground-chat-line: var(--play-line);
  --ui-playground-room-width: 220px;
  flex: 0 0 clamp(280px, 28vw, 640px);
  width: auto;
  container: playground-radio / inline-size;
}
.ui-playground-chat {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  flex: 1;
  min-width: 0;
  min-height: 0;
}
.ui-playground-radio :is(.chat-list-pane, .chat-thread-pane) { width: auto; }
.ui-playground-chat:where(.thread-open) > .chat-list-pane,
.ui-playground-chat:where(:not(.thread-open)) > .chat-thread-pane { display: none; }
.ui-playground-radio .chat-rooms-back { display: inline-flex; }
/* 520px leaves 300px for the thread beside the 220px room list. */
@container playground-radio (min-width: 520px) {
  .ui-playground-chat { grid-template-columns: var(--ui-playground-room-width) minmax(0, 1fr); }
  .ui-playground-chat > :is(.chat-list-pane, .chat-thread-pane) { display: flex; }
  .ui-playground-radio .chat-list-pane { border-right: 1px solid var(--ui-playground-chat-line); }
  .ui-playground-radio .chat-thread-header { display: none; }
}
.ui-playground-avatar {
  display: grid;
  place-items: center;
  width: var(--chat-avatar);
  height: var(--chat-avatar);
  border-radius: 0;
  background: var(--skin-accent);
  color: var(--skin-panel);
  font-weight: 700;
}
.ui-playground-world {
  position: relative;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  background: var(--surface-0);
}
.ui-playground-backdrop {
  position: absolute;
  inset: 0;
  background: center / cover no-repeat;
  filter: blur(3px) brightness(.62);
}
.ui-playground-world .play-top-row { margin: 0 auto; }
.ui-playground-scene {
  position: absolute;
  inset: 110px 0 var(--ui-playground-menu-space);
  overflow-y: auto;
}
.ui-playground-build {
  --panel-tabline-height: 54px;
  position: absolute;
  bottom: var(--ui-playground-menu-space);
  left: 50%;
  width: var(--ui-playground-width, min(var(--float-width), 92%));
  height: min(55%, var(--panel-fixed-max));
  transform: translateX(-50%);
  transition: none;
}
.ui-playground-build .panel-body { flex: 1; }
.ui-playground-menu {
  position: absolute;
  bottom: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: var(--ui-playground-width, min(var(--float-width), 92%));
  padding: 0;
  background: transparent;
  pointer-events: auto;
}
@media (max-width: 700px) {
  .ui-playground-radio { flex-basis: 38%; }
  .ui-playground-menu { max-width: 100%; overflow: auto; }
}

/* Tracker views use the same panel surfaces and controls as the rest of play. */
.tracker-panel-body { flex: 1; overflow: hidden; }
.tracker-view { display: flex; flex: 1; flex-direction: column; gap: 12px; min-width: 0; min-height: 0; overflow: auto; }
.tracker-view.tracker-board-open { overflow: hidden; }
.tracker-view .row, .tracker-view .card-meta, .tracker-view .entry-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.tracker-board { display: flex; flex: 1; flex-direction: column; gap: 10px; min-height: 0; }
.tracker-statuses { display: flex; gap: 6px; overflow-x: auto; flex: none; padding-bottom: 4px; scrollbar-width: thin; scrollbar-color: var(--scroll) transparent; }
.tracker-status { flex: none; display: flex; align-items: center; gap: 6px; }
.tracker-status-count { color: var(--status, var(--muted)); font-weight: 700; }
.tracker-ticket-list { display: grid; align-content: start; gap: 8px; overflow-y: auto; min-height: 0; scrollbar-width: thin; scrollbar-color: var(--scroll) transparent; }
.tracker-empty { color: var(--muted); margin: 4px 0; }
.tracker-view .card, .tracker-view .entry, .tracker-view .block { display: block; min-width: 0; padding: 10px; border: 1px solid var(--panel-border, var(--line)); border-radius: 8px; background: var(--surface-1, var(--panel)); color: var(--ink); }
.tracker-view .card { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; text-decoration: none; }
.tracker-view .card-title { grid-column: 1; grid-row: 1; }
.tracker-view .card-when { grid-column: 2; grid-row: 1; }
.tracker-view .card-meta { grid-column: 1 / -1; }
.tracker-view .card-title, .tracker-view .ticket-title { overflow-wrap: anywhere; }
.tracker-view .card-meta, .tracker-view .byline, .tracker-view .card-when, .tracker-view .muted { color: var(--muted); font-size: var(--meta-text-size); }
.tracker-view .tag { display: inline-block; padding: 2px 5px; border: 1px solid var(--line); border-radius: 4px; }
.tracker-view .entry { margin-top: 8px; }
.tracker-view .entry-actions { margin-left: auto; display: flex; gap: 4px; }
.tracker-view .prose { white-space: pre-wrap; overflow-wrap: anywhere; }
.tracker-view textarea, .tracker-view input { max-width: 100%; }
.tracker-view textarea { width: 100%; }
.tracker-view h1, .tracker-view h3 { margin: 0 0 8px; }

/* A player's report on a ticket: their crest and name over their words, and beside the reporter's
   own report, a box with what only they may do. */
.ticket-filing { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 14px; margin-top: 8px; padding: 10px; border: 1px solid var(--play-line); border-radius: 8px; background: var(--surface-2); }
.ticket-filing-words { flex: 1 1 240px; min-width: 0; }
.ticket-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; font-size: var(--meta-text-size); color: var(--muted); }
.ticket-actions { margin-bottom: 4px; }
.ticket-filing-who { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.ticket-filing-when { align-self: flex-start; margin-left: auto; font-size: var(--meta-text-size); color: var(--muted); white-space: nowrap; }
.ticket-filing-flags { font-size: var(--meta-text-size); color: var(--muted); }
.ticket-filing-profile { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 0; border: 0; background: none; color: inherit; text-align: left; cursor: pointer; }
.ticket-filing-profile:hover .ticket-filing-name, .ticket-filing-profile:focus-visible .ticket-filing-name { text-decoration: underline; }
.ticket-filing-meta { display: flex; flex-direction: column; min-width: 0; }
.ticket-filing-name { font-size: var(--card-title-size); color: var(--text); overflow-wrap: anywhere; }
.ticket-filing-own { flex: none; display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border: 1px solid var(--play-line-strong); border-radius: 8px; background: var(--surface-1); }
.ticket-filing-own h4 { margin: 0; font-size: var(--caption-text-size); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; }

/* What the developer wrote about a ticket, under the gear in the developer's colour. */
.tracker-view .ticket-developer { border-color: color-mix(in srgb, var(--developer) 45%, transparent); background: color-mix(in srgb, var(--developer) 8%, var(--surface-1)); box-shadow: inset 3px 0 0 var(--developer); }
.ticket-developer-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.tracker-view .ticket-developer-head h3 { margin: 0; color: var(--developer); }
.ticket-developer-badge { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; background: color-mix(in srgb, var(--developer) 24%, transparent); color: var(--developer); }
.ticket-developer-badge .i { width: 60%; height: 60%; }
.ticket-note { margin-top: 10px; padding: 8px 10px; border: 1px solid var(--play-line); border-radius: 8px; background: var(--surface-1); }
.ticket-note p { margin: 0; }
.ticket-note h4, .ticket-log-title { margin: 0 0 4px; font-size: var(--caption-text-size); color: var(--developer); text-transform: uppercase; letter-spacing: 0.04em; }
.ticket-log-title { margin-top: 14px; }

/* The ticket's log: the lines the tracker posted in its room, each with the System's clanker. */
.ticket-log { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
.ticket-log-line { display: flex; align-items: flex-start; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--surface-1); }
.ticket-log-mark { display: grid; place-items: center; flex: none; width: 22px; height: 22px; border-radius: 6px; background: color-mix(in srgb, var(--system) 20%, transparent); color: var(--system); }
.ticket-log-mark .i { width: 70%; height: 70%; }
.ticket-log-text { flex: 1 1 auto; min-width: 0; }
.ticket-log-when { flex: none; font-size: var(--meta-text-size); color: var(--muted); white-space: nowrap; }

.screen-play .site-rewards {
  grid-column: 1 / -1;
  color: var(--text-dim);
  margin-top: 4px;
}

.screen-play .site-rewards[hidden] {
  display: none;
}

.screen-play .site-reward-list {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 5px;
}

.screen-play .site-reward {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 6px;
  border-radius: 6px;
  color: var(--ok-text);
  background: color-mix(in srgb, var(--ok) 16%, var(--chip-well));
  outline: 1px dashed color-mix(in srgb, var(--ok) 70%, transparent);
  outline-offset: -1px;
  font-size: var(--caption-text-size);
  white-space: nowrap;
}

.screen-play .site-reward .i {
  width: 16px;
  height: 16px;
}

.screen-play .hud-bar {
  box-shadow: none;
  background: var(--skin-panel);
}

.screen-play .card-title {
  text-shadow: none;
  filter: none;
}

@media (max-width: 1000px) {
  .screen-play .menu-bar button { font-size: 11px; padding: 4px; }
  .screen-play .menu-bar .dest-item { flex-direction: column; gap: 2px; }
  .screen-play .chips { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; }
  .screen-play .res-val { white-space: nowrap; }
  .screen-play .res-line { flex-wrap: wrap; gap: 0 3px; }
  .screen-play .city-sheet-title { font-size: 24px; line-height: 1.1; margin: 0; }
}
.ui-playground-world { container: amber-world / inline-size; }
.ui-playground-world .play-top-row { width: var(--play-width); }
.ui-playground-build { width: var(--play-width); height: 38%; border: 0; border-top: 5px solid var(--skin-accent); padding: 16px; }
.ui-playground-menu { bottom: 0; padding-block: 8px; }
@container amber-world (min-width: 744px) {
  .ui-playground-world .play-top-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; }
  .ui-playground-world .play-top-controls { position: relative; inset: auto; align-items: stretch; margin-top: var(--play-top-gap); }
  .ui-playground-world .play-right { flex: 1; align-items: stretch; }
  .ui-playground-world .clock { flex: 1; }
  .ui-playground-world .hud-tools { position: absolute; top: calc(100% + var(--play-top-gap)); right: 0; }
}
html.aspect-portrait .screen-play { --play-width: 100%; --play-control-inset: 7px; }
:where(html.aspect-portrait) .ui-playground-radio { display: none; }
:where(html.aspect-portrait) .ui-playground-menu { height: var(--ui-playground-menu-space); background: var(--skin-panel); }
