/* Sizing uses rem, fractions and viewport units so the whole tool scales with
   the root font size instead of being pinned to pixel values. The root size is
   itself relative: rem inside the root's own font-size means the size the
   browser was set to, so a reader who has raised that keeps the increase, and
   --ui-scale multiplies whatever that comes to. */

:root {
  --bg: #0a0d12;
  --bg-panel: #111721;
  --bg-raised: #18202c;
  --line: #243043;
  --text: #dbe3ee;
  --text-dim: #8d9bb0;
  --accent: #7fd1a0;
  --accent-warm: #ffc978;
  --danger: #e0745f;
  --radius: 0.4rem;
  --gap: 0.6rem;
  --ui-scale: 1;
  --hairline: 0.0625rem;
  font-size: calc(clamp(0.8rem, 0.55vw + 0.65rem, 1.06rem) * var(--ui-scale));
  /* Lets a height ease to auto, which is what animates the section folds; a
     browser without it snaps them shut the way they always did. */
  interpolate-size: allow-keywords;
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.topbar {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--gap);
  padding: 0.5rem 0.9rem;
  border-bottom: var(--hairline) solid var(--line);
  background: var(--bg-panel);
  flex: 0 0 auto;
}

.topbar h1 {
  margin: 0;
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  color: var(--accent);
}

/* The build, beside the name. Small enough to ignore and always there to
   quote when something looks wrong. */
.version {
  margin-left: 0.4rem;
  font-size: 0.65rem;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  vertical-align: super;
}

.subtitle {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.8rem;
}

.topbar-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  /* The bar carries enough controls to outgrow a phone, and a row that will
     not wrap pushes the last of them off the side of the page. */
  flex-wrap: wrap;
  gap: 0.4rem;
}

.save-note {
  color: var(--text-dim);
  font-size: 0.75rem;
}

.layout {
  flex: 1 1 auto;
  display: grid;
  /* The menu track is one length so folding it away can ease to another:
     --panel-w is where the drag handle left it, the clamp keeps both the
     drag and a narrowed window honest. */
  grid-template-columns: clamp(18rem, var(--panel-w, 27rem), 60vw) 1fr;
  min-height: 0;
  transition: grid-template-columns 0.25s ease, grid-template-rows 0.25s ease;
}

/* While the handle is being dragged the pointer leads; easing would trail. */
.resizing .layout {
  transition: none;
}

@media (max-width: 60rem) {
  .layout {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr 1fr;
  }
}

/* Just the world: every piece of chrome goes, leaving the stage and one way
   back out of it. Taken out of the layout rather than hidden in place, so the
   stage reflows into the space each piece leaves. */
.stage-only .topbar,
.stage-only .panel,
.stage-only .stage-toolbar,
.stage-only .statusbar {
  display: none;
}

.stage-only .layout {
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
}

/* Faint until it is looked for, so it does not sit on top of the thing it is
   there to get out of the way of, and gone altogether once the pointer has been
   still for a while. Any movement brings it back. */
.stage-escape {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  z-index: 2;
  opacity: 0.25;
  transition: opacity 0.6s ease;
}

.stage-escape:hover,
.stage-escape:focus-visible {
  opacity: 1;
  transition: opacity 0.15s ease;
}

.stage-only.idle .stage-escape:not(:focus-visible) {
  opacity: 0;
  pointer-events: none;
}

body:not(.stage-only) .stage-escape {
  display: none;
}

/* Driving a person: the stick and what they can be asked to do, over the map
   rather than in the panel, because it is played with rather than set. The bar
   itself lets presses through; only the controls in it take them. */
.stage-hud {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  gap: var(--gap);
  padding: var(--gap);
  pointer-events: none;
}

.stage-hud > * {
  pointer-events: auto;
}

/* A class that sets `display` beats the browser's own rule for [hidden], so
   the bar has to say it itself or hiding it does nothing. */
.stage-hud[hidden] {
  display: none;
}

.hud-row {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.3rem;
  margin-left: auto;
}

.hud-acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.3rem;
}

.hud-who {
  padding: 0.15rem 0.45rem;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg-panel) 82%, transparent);
  color: var(--text-dim);
  font-size: 0.78rem;
}

/* A ring with a knob in it. Sized in rem so it follows the text scale, and
   `touch-action: none` so dragging it is not read as scrolling the page. */
.stick {
  display: grid;
  place-items: center;
  width: 7rem;
  height: 7rem;
  border: var(--hairline) solid var(--line);
  border-radius: 50%;
  background: color-mix(in srgb, var(--bg-panel) 60%, transparent);
  touch-action: none;
}

.stick-knob {
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 55%, var(--bg-raised));
  border: var(--hairline) solid var(--line);
  transition: transform 0.08s linear;
}

@media (prefers-reduced-motion: reduce) {
  .stick-knob {
    transition: none;
  }
}

/* Settled into the window on its own rather than because somebody asked for it.
   There is nothing to press to get out of it: the first sign of life anywhere,
   a moved pointer included, hands the menus straight back. */
.settled .stage-escape {
  display: none;
}

/* Folded away, the map gets the window. The menu's track eases to nothing and
   the panel fades as it goes; visibility waits for the slide so the fold does
   not blink, then takes the hidden controls out of the tab order. */
.menu-collapsed .layout {
  grid-template-columns: 0rem 1fr;
}

.panel {
  opacity: 1;
  transition: opacity 0.2s ease, visibility 0s linear 0s;
}

.menu-collapsed .panel {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, visibility 0s linear 0.25s;
}

@media (max-width: 60rem) {
  .menu-collapsed .layout {
    grid-template-columns: 1fr;
    grid-template-rows: 0fr 1fr;
  }
}

.topbar-actions .scale-box {
  width: 3.2rem;
  flex: 0 0 auto;
}

.topbar-actions .unit {
  color: var(--text-dim);
  font-size: 0.72rem;
}

.topbar-actions .inline input[type="range"] {
  width: clamp(3.5rem, 6vw, 7rem);
}

.panel {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  border-right: var(--hairline) solid var(--line);
  background: var(--bg-panel);
}

/* Held at the menu's narrowest while the track slides shut, so the fold is a
   curtain drawn over the controls rather than the controls rewrapping. */
.panel > :not(.panel-resize) {
  min-width: 18rem;
}

/* The handle on the menu's edge: the whole strip drags, the grip in the
   middle is what says so. */
.panel-resize {
  position: absolute;
  top: 0;
  right: 0;
  width: 0.5rem;
  height: 100%;
  cursor: col-resize;
  touch-action: none;
  z-index: 3;
}

.panel-resize::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0.12rem;
  transform: translateY(-50%);
  width: 0.25rem;
  height: 2.6rem;
  border-radius: 0.2rem;
  background: var(--line);
  transition: background 0.15s ease;
}

.panel-resize:hover::after,
.panel-resize.dragging::after {
  background: var(--text-dim);
}

/* Stacked layouts split the window top to bottom, and the fold takes the
   whole menu anyway: nothing for a width handle to do. */
.menu-collapsed .panel-resize {
  display: none;
}

@media (max-width: 60rem) {
  .panel-resize {
    display: none;
  }
}

.tabs {
  display: flex;
  gap: 0.2rem;
  padding: 0.4rem 0.4rem 0;
  border-bottom: var(--hairline) solid var(--line);
  flex: 0 0 auto;
}

.tab {
  flex: 1 1 auto;
  padding: 0.4rem 0.5rem;
  background: transparent;
  border: var(--hairline) solid transparent;
  border-bottom: none;
  border-radius: var(--radius) var(--radius) 0 0;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 0.85rem;
}

.tab.active {
  background: var(--bg-raised);
  border-color: var(--line);
  color: var(--text);
}

/* The thin strip over the panel body: the fold-all button, right where the
   sections it pulls on begin. Gone whole when the button has nothing to do. */
.panel-tools {
  display: flex;
  justify-content: flex-end;
  padding: 0.25rem 0.4rem;
  border-bottom: var(--hairline) solid var(--line);
  flex: 0 0 auto;
}

.panel-tools:has(> [hidden]) {
  display: none;
}

.panel-tools .fold-all {
  padding: 0.15rem 0.5rem;
  font-size: 0.72rem;
  color: var(--text-dim);
  background: transparent;
}

.panel-tools .fold-all:hover {
  color: var(--text);
}

.panel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

.group {
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
}

/* The head is a summary: pressing it folds the section. */
.group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  border-bottom: var(--hairline) solid var(--line);
  cursor: pointer;
  list-style: none;
}

.group-head::-webkit-details-marker {
  display: none;
}

/* The same cut triangle the view menu turns, pointing along at the far end
   of the head. */
.group-head::after {
  content: "";
  flex: 0 0 auto;
  width: 0.5em;
  height: 0.5em;
  background: var(--text-dim);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  transition: transform 0.12s ease;
}

details.group[open] > .group-head::after {
  transform: rotate(90deg);
}

details.group:not([open]) > .group-head {
  border-bottom: 0;
}

/* The fold slides and fades rather than snapping. block-size to auto only
   eases where interpolate-size is understood; elsewhere this whole block is
   inert and the fold snaps as before. */
details.group::details-content {
  block-size: 0;
  overflow-y: clip;
  opacity: 0;
  transition:
    block-size 0.2s ease,
    opacity 0.2s ease,
    content-visibility 0.2s allow-discrete;
}

details.group[open]::details-content {
  block-size: auto;
  opacity: 1;
}

/* Copy and Save, at the far end of the head beside the fold triangle. They
   are dim until the head is under the pointer, so a column of sections reads
   as a column of names rather than as a column of buttons. */
.group-tools {
  display: flex;
  gap: 0.25rem;
  margin-left: auto;
  opacity: 0.25;
  transition: opacity 0.12s ease;
}

.group-head:hover .group-tools,
.group-tools:focus-within {
  opacity: 1;
}

.btn.tiny {
  padding: 0.1rem 0.35rem;
  font-size: 0.68rem;
  line-height: 1.3;
}

.group-head h3 {
  margin: 0;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}

.group-body {
  padding: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.field {
  display: grid;
  grid-template-columns: 45% 1fr;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
}

.field-label {
  color: var(--text-dim);
}

.field-hint {
  grid-column: 1 / -1;
  color: #6d7c92;
  font-size: 0.72rem;
  line-height: 1.3;
}

.num-pair {
  display: grid;
  grid-template-columns: 1fr 4.5rem;
  gap: 0.3rem;
  align-items: center;
}

.range-pair {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 0.3rem;
  align-items: center;
  font-size: 0.72rem;
  color: var(--text-dim);
}

input[type="number"],
input[type="text"],
select {
  width: 100%;
  min-width: 0;
  background: #0c1017;
  color: var(--text);
  border: var(--hairline) solid var(--line);
  border-radius: 0.25rem;
  padding: 0.2rem 0.3rem;
  font-size: 0.8rem;
  font-family: inherit;
}

input[type="range"] {
  width: 100%;
  min-width: 0;
  accent-color: var(--accent);
}

input[type="color"] {
  width: 2.4rem;
  height: 1.6rem;
  padding: 0;
  border: var(--hairline) solid var(--line);
  background: none;
  border-radius: 0.25rem;
}

.btn {
  background: #1d2735;
  color: var(--text);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 0.3rem 0.6rem;
  font-size: 0.8rem;
  cursor: pointer;
  font-family: inherit;
}

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

.btn.active {
  background: var(--accent);
  color: #08131c;
  border-color: var(--accent);
}

.btn.danger:hover {
  border-color: var(--danger);
  color: var(--danger);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  align-items: center;
}

.note {
  margin: 0;
  color: #6d7c92;
  font-size: 0.74rem;
  line-height: 1.4;
}

.inline {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  color: var(--text-dim);
}

/* materials */

.editor-wrap {
  width: 100%;
  aspect-ratio: 2 / 1;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: #0c1017;
}

.grid-canvas {
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  cursor: crosshair;
}

.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem;
}

.swatch {
  width: 1.15rem;
  height: 1.15rem;
  border: var(--hairline) solid rgba(255, 255, 255, 0.18);
  border-radius: 0.15rem;
  cursor: pointer;
  padding: 0;
}

/* One row per height of the box, top of the box at the top, so the strip reads
   as the thing that will be drawn from it rather than as a palette. */
.ramp-strip {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 4rem;
  border: var(--hairline) solid var(--line);
  border-radius: 0.25rem;
  overflow: hidden;
}

.ramp-row {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}

.ramp-cell {
  flex: 1 1 auto;
}

.sampler-list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-height: 16rem;
  overflow-y: auto;
}

.sampler-item {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: 0.5rem;
  align-items: center;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 0.3rem;
  cursor: pointer;
  text-align: left;
  color: var(--text);
}

.sampler-item.active {
  border-color: var(--accent-warm);
}

.sampler-meta {
  display: flex;
  flex-direction: column;
  font-size: 0.76rem;
}

.sampler-meta span {
  color: var(--text-dim);
  font-size: 0.7rem;
}

.thumb {
  width: 100%;
  height: 1.6rem;
  border-radius: 0.15rem;
  display: block;
}

/* person sprites */

.sprite-slot {
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  padding: 0.5rem 0.6rem;
  margin-bottom: var(--gap);
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.sprite-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap);
}

.sprite-name {
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--accent);
}

.sprite-meta {
  font-size: 0.72rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.dropzone {
  border: var(--hairline) dashed var(--line);
  border-radius: var(--radius);
  /* A checker so transparent frames read as cut out rather than as black. */
  background-color: #0d1219;
  background-image:
    linear-gradient(45deg, #161d27 25%, transparent 25%),
    linear-gradient(-45deg, #161d27 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #161d27 75%),
    linear-gradient(-45deg, transparent 75%, #161d27 75%);
  background-size: 0.5rem 0.5rem;
  background-position: 0 0, 0 0.25rem, 0.25rem -0.25rem, -0.25rem 0;
  padding: 0.5rem;
  min-height: 3.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  cursor: pointer;
  text-align: center;
}

/* The strip and the hint are never the target of a drag: a pointer crossing
   onto a child would otherwise read as leaving the zone and drop the
   highlight. */
.dropzone > * {
  pointer-events: none;
}

.dropzone:hover,
.dropzone:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.dropzone.over {
  border-color: var(--accent);
  border-style: solid;
  box-shadow: inset 0 0 0 0.1rem rgba(127, 209, 160, 0.25);
}

/* The editor's own zone sits under a canvas that is already the tall thing in
   the panel, so it asks for as little height as it can be read at. */
.dropzone.short {
  min-height: 2.2rem;
  padding: 0.35rem;
}

.dropzone-hint {
  font-size: 0.72rem;
  color: var(--text-dim);
}

.sprite-strip {
  height: 3.5rem;
  width: auto;
  max-width: 100%;
  image-rendering: pixelated;
  display: block;
}

/* The picture laid over the map to draw zones from. It is dragged on, so it
   must not be read as a page scroll on a touch screen, and it is a landscape
   rather than pixel art: the browser may smooth it. */
.landscape {
  display: block;
  width: 100%;
  height: auto;
  max-height: 40vh;
  object-fit: contain;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  touch-action: none;
  cursor: crosshair;
}

/* shading */

.plot-wrap,
.preview-wrap {
  width: 100%;
  aspect-ratio: 2 / 1;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.preview-wrap.tall {
  aspect-ratio: 4 / 3;
}

.plot-canvas,
.shade-preview,
.species-preview {
  width: 100%;
  height: 100%;
  display: block;
}

/* species */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

/* What a row of chips is, said once above it. Takes the whole first line of
   the row it sits in. */
.chip-head {
  flex: 1 0 100%;
  margin: 0;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.chip-block {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.chip {
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: 1rem;
  color: var(--text);
  padding: 0.2rem 0.6rem;
  font-size: 0.76rem;
  cursor: pointer;
}

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

/* The color a brush paints with, beside its name in the map editor's legend.
   A span rather than a button, so it has to be told to take a size at all. */
.brush-dot {
  display: inline-block;
  width: 0.72rem;
  height: 0.72rem;
  margin-right: 0.35rem;
  vertical-align: -0.08em;
  border: var(--hairline) solid rgba(255, 255, 255, 0.22);
  border-radius: 0.15rem;
}

.chip.off {
  opacity: 0.45;
}

.chip.dim {
  color: var(--text-dim);
  cursor: default;
}

.class-block {
  border-top: var(--hairline) solid var(--line);
  padding-top: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.class-block h4 {
  margin: 0;
  font-size: 0.78rem;
  color: var(--accent-warm);
}

/* stage */

.stage {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}

.stage-toolbar {
  border-bottom: var(--hairline) solid var(--line);
  background: var(--bg-panel);
  padding: 0.35rem 0.6rem;
  flex: 0 0 auto;
}

.toolbar-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.toolbar-row input[type="range"] {
  width: clamp(5rem, 10vw, 11rem);
}

.readout {
  color: var(--text-dim);
  font-size: 0.75rem;
  min-width: 2.6rem;
}

.stage-canvas-wrap {
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  background: #05070a;
}

#world-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  cursor: grab;
}

#world-canvas:active {
  cursor: grabbing;
}

.statusbar {
  flex: 0 0 auto;
  border-top: var(--hairline) solid var(--line);
  background: var(--bg-panel);
  padding: 0.3rem 0.6rem;
  color: var(--text-dim);
  font-size: 0.74rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  overflow-x: auto;
}

/* modes */

.modes {
  display: flex;
  gap: 0.25rem;
  padding: 0.4rem 0.4rem 0;
  flex: 0 0 auto;
}

.mode {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.35rem 0.4rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  color: var(--text-dim);
  cursor: pointer;
  font-size: 0.8rem;
  font-family: inherit;
  letter-spacing: 0.04em;
}

.mode.active {
  color: #08131c;
  background: var(--accent);
  border-color: var(--accent);
}

/* settlement panels */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.25rem;
}

/* The layers dropped on the map page: one row each, holding its name and
   size, what it is read as, and a way off the list. Not the sheet's layer
   list, which has rows of its own further down. */
.map-layer-list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* The name takes a line of its own over the select and the button, so a
   long file name is read whole rather than squeezed beside a wide select. */
.map-layer-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content;
  align-items: center;
  gap: 0.3rem 0.35rem;
  padding: 0.25rem 0.4rem;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
}

.map-layer-row .map-layer-name {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  font-size: 0.8rem;
  overflow-wrap: anywhere;
}

.map-layer-row .map-layer-size {
  font-size: 0.7rem;
  color: var(--text-dim);
}

.map-layer-row select {
  grid-column: 1;
  grid-row: 2;
  min-width: 0;
  width: 100%;
}

.map-layer-row .btn {
  grid-column: 2;
  grid-row: 2;
  width: auto;
  justify-self: end;
}

.stat {
  display: flex;
  justify-content: space-between;
  gap: 0.4rem;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: 0.25rem;
  padding: 0.2rem 0.4rem;
  font-size: 0.75rem;
}

.stat-key {
  color: var(--text-dim);
}

.stat-val {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.roster {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  max-height: 22rem;
  overflow-y: auto;
}

.roster-row {
  display: grid;
  grid-template-columns:
    minmax(6rem, 1.4fr) minmax(5rem, 1fr) minmax(5rem, 1.2fr)
    minmax(2.5rem, auto) 2.5rem 2.5rem;
  align-items: center;
  gap: 0.3rem;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: 0.25rem;
  padding: 0.2rem 0.35rem;
  font-size: 0.72rem;
}

.roster-row.active {
  border-color: var(--accent);
}

.roster-row.dead {
  opacity: 0.5;
}

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

/* A name in the register opens that person's record. */
.roster-name.link {
  background: none;
  border: none;
  padding: 0;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.roster-name.link:hover {
  color: var(--accent);
}

.roster-job,
.roster-task {
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.roster-coin {
  color: var(--accent-warm);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: right;
}

/* One person's record: who they are, how they are and what has happened. */
.person-card {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  background: #0d131b;
  border: var(--hairline) solid var(--line);
  border-radius: 0.3rem;
  padding: 0.4rem 0.5rem;
}

.person-card:empty {
  display: none;
}

.person-bars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.2rem 0.6rem;
}

.person-bars .field {
  display: grid;
  grid-template-columns: 4.5rem 1fr;
  align-items: center;
  gap: 0.35rem;
}

.bar {
  display: block;
  height: 0.45rem;
  background: #0a0f16;
  border: var(--hairline) solid var(--line);
  border-radius: 0.2rem;
  overflow: hidden;
}

.bar-fill {
  display: block;
  height: 100%;
  background: var(--accent);
}

.bar-fill.hunger {
  background: #cbd06a;
}

.bar-fill.health {
  background: #7fd1a0;
}

.bar-fill.build {
  background: var(--accent-warm);
}

.bar-fill.research {
  background: #7fb4ff;
}

.bar-fill.fed {
  background: #cbd06a;
}

.bar-fill.rested {
  background: #9ba7f0;
}

.bar-fill.content {
  background: #e2a4c8;
}

.bar-fill.calm {
  background: #8fb8c8;
}

/* Who a person knows, strongest feeling first either way. */
.bonds {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.bond {
  display: grid;
  grid-template-columns: minmax(6rem, 1.4fr) minmax(3.5rem, 4.5rem) 1fr;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.72rem;
}

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

.bond-tie {
  color: var(--text-dim);
}

.bond-tie.friend {
  color: #7fd1a0;
}

.bond-tie.rival {
  color: #e08a7a;
}

.bond-tie.married,
.bond-tie.kin {
  color: var(--accent-warm);
}

.bar-fill.friend {
  background: #7fd1a0;
}

.bar-fill.rival {
  background: #e08a7a;
}

.catalog,
.tech-tree {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.cat-row,
.tech-row {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 0.3rem 0.4rem;
}

.cat-row.locked,
.tech-row.locked {
  opacity: 0.55;
}

.tech-row.known {
  border-color: rgba(127, 209, 160, 0.4);
}

.tech-row.target {
  border-color: var(--accent-warm);
}

.cat-head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.cat-name {
  font-size: 0.8rem;
  flex: 1 1 auto;
}

.cat-count {
  color: var(--accent-warm);
  font-size: 0.72rem;
}

.cat-cost {
  color: var(--text-dim);
  font-size: 0.72rem;
}

.cat-meta,
.cat-note {
  color: #6d7c92;
  font-size: 0.7rem;
  line-height: 1.3;
}

.cat-lock {
  color: var(--danger);
  font-size: 0.7rem;
}

.sub-head {
  margin: 0.3rem 0 0;
  font-size: 0.76rem;
  color: var(--accent-warm);
}

.stock-table {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
}

.stock-row {
  display: grid;
  grid-template-columns: 0.8rem minmax(4rem, 1.4fr) repeat(5, minmax(2.4rem, 1fr));
  align-items: center;
  gap: 0.25rem;
  font-size: 0.72rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.stock-row.head {
  color: var(--text-dim);
  border-bottom: var(--hairline) solid var(--line);
  padding-bottom: 0.15rem;
}

.stock-name {
  font-family: inherit;
}

.stock-num {
  text-align: right;
}

.stock-num.dim {
  color: var(--text-dim);
}

.stock-num.up {
  color: var(--accent);
}

.stock-num.down {
  color: var(--danger);
}

.swatch-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  border: var(--hairline) solid rgba(255, 255, 255, 0.2);
}

.swatch-dot.ghost {
  border-color: transparent;
  background: none;
}

.event-log {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  max-height: 9rem;
  overflow-y: auto;
}

.event {
  color: var(--text-dim);
  font-size: 0.7rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* color wheel */

.wheel {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.wheel-canvas {
  width: 100%;
  max-width: 11rem;
  aspect-ratio: 1 / 1;
  align-self: center;
  display: block;
  touch-action: none;
  cursor: crosshair;
}

input[type="text"].hex {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  text-transform: lowercase;
}

/* sprite editor */

/* Drawing on the stage rather than dragging it: the pointer is a brush there,
   and the middle button or a held control key is what drags. */
body.painting #world-canvas {
  cursor: crosshair;
}

/* Picking people up off the map rather than dragging it. */
body.moving-people #world-canvas {
  cursor: grab;
}

body.moving-people.holding #world-canvas {
  cursor: grabbing;
}

body.harvesting #world-canvas {
  cursor: crosshair;
}

/* Setting somebody new down where the press lands. */
body.adding-people #world-canvas {
  cursor: copy;
}

/* Looking inside whatever building the press lands on. */
body.inspecting #world-canvas {
  cursor: zoom-in;
}

.readout.hint {
  min-width: 0;
  opacity: 0.75;
}

.layer-list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.layer-row {
  display: grid;
  grid-template-columns: 3.2rem 1fr auto;
  gap: 0.4rem;
  align-items: center;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 0.25rem 0.35rem;
}

.layer-row.active {
  border-color: var(--accent-warm);
}

/* The thumbnail is the target that picks the layer, so it carries no chrome of
   its own; the row around it is what reads as selected. */
.layer-pick {
  background: #0a0f16;
  border: var(--hairline) solid var(--line);
  border-radius: 0.2rem;
  padding: 0;
  cursor: pointer;
  display: block;
  overflow: hidden;
}

.layer-pick .thumb {
  height: 2rem;
}

/* Sheets kept outside the project: what it is, and the two things that can be
   done to it. */
.kept-list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-height: 14rem;
  overflow-y: auto;
}

.kept-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 0.4rem;
  align-items: center;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 0.3rem 0.4rem;
}

.frame-strip {
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  padding-bottom: 0.2rem;
}

.frame-cell {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  background: #0f151e;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  padding: 0.2rem;
  cursor: pointer;
  color: var(--text-dim);
}

.frame-cell.active {
  border-color: var(--accent-warm);
  color: var(--text);
}

.frame-thumb {
  width: 2.6rem;
  height: 2.6rem;
  display: block;
}

.frame-index {
  font-size: 0.66rem;
  font-variant-numeric: tabular-nums;
}

.sheet-preview {
  width: 100%;
  height: 100%;
  display: block;
  background: #0c1017;
}

/* Choosing a sheet to send to a person motion. */
.sprite-from {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.4rem;
  align-items: end;
}

.sprite-from .field {
  margin: 0;
}

.btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.btn:disabled:hover {
  border-color: var(--line);
}

/* ---- menu search --------------------------------------------------------
   The box sits in the top bar and its results hang under it, over the page,
   so finding a setting never moves anything that was already on screen. */

.find {
  position: relative;
  flex: 1 1 16rem;
  max-width: 28rem;
  align-self: center;
}

/* The box and the switch that widens it read as one control, so they sit in
   one row and the results hang under both of them. */
.find-row {
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
}

.find-meaning {
  flex: 0 0 auto;
  font-size: 0.75rem;
}

.find-box {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.3rem 0.5rem;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
}

.find-box:focus {
  outline: none;
  border-color: var(--accent);
}

.find-results {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.25rem);
  left: 0;
  right: 0;
  margin: 0;
  padding: 0.2rem;
  list-style: none;
  max-height: 60vh;
  overflow-y: auto;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-panel);
  box-shadow: 0 0.5rem 1.5rem #0009;
}

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

.find-hit {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  padding: 0.3rem 0.45rem;
  border-radius: var(--radius);
  cursor: pointer;
}

.find-hit.on,
.find-hit:hover {
  background: var(--bg-raised);
}

.find-label {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.85rem;
}

/* Marks a row the letters typed could not have found, so an answer that looks
   like it came from nowhere says where it came from. */
.find-why {
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-warm);
}

.find-path {
  font-size: 0.7rem;
  color: var(--text-dim);
}

.find-empty {
  padding: 0.35rem 0.45rem;
  font-size: 0.8rem;
  color: var(--text-dim);
}

/* Where search just sent you. Fades on its own so nothing has to clean it up
   on a timer. */
.found {
  animation: find-flash 1.8s ease-out;
  border-radius: var(--radius);
}

@keyframes find-flash {
  0%,
  40% {
    background: #7fd1a033;
    box-shadow: 0 0 0 0.2rem #7fd1a033;
  }
  100% {
    background: transparent;
    box-shadow: 0 0 0 0.2rem transparent;
  }
}

.stage-only .find {
  display: none;
}

/* ---- the view menu ------------------------------------------------------
   What the stage draws over the map. A dropdown in the top bar rather than a
   block of the side panel: it is about the stage, not the project, and it
   stays reachable when the side menu is folded away. */

.view-menu {
  position: relative;
  align-self: center;
  flex: 0 0 auto;
}

.view-menu[hidden] {
  display: none;
}

.view-menu > summary {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3rem 0.6rem;
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--text-dim);
  background: #1d2735;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  list-style: none;
}

.view-menu > summary:hover,
.view-menu[open] > summary {
  color: var(--text);
}

.view-menu > summary:focus-visible {
  outline: var(--hairline) solid var(--accent);
  outline-offset: calc(var(--hairline) * -2);
}

.view-menu > summary::-webkit-details-marker {
  display: none;
}

/* A triangle cut out of a square rather than a glyph: a character has its own
   idea of a baseline and of how much room to leave beside itself, and neither
   survives being rotated. This one turns about its own middle. */
.view-menu > summary::before {
  content: "";
  flex: 0 0 auto;
  width: 0.5em;
  height: 0.5em;
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  transition: transform 0.12s ease;
}

.view-menu[open] > summary::before {
  transform: rotate(90deg);
}

/* The dropdown fades in and out. Only opacity moves: a transform on the
   content wrapper would become the containing block of the absolutely
   positioned body and pull it out of place. */
.view-menu::details-content {
  opacity: 0;
  transition: opacity 0.15s ease, content-visibility 0.15s allow-discrete;
}

.view-menu[open]::details-content {
  opacity: 1;
}

.view-body {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  z-index: 40;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  width: max-content;
  max-width: min(24rem, 90vw);
  padding: 0.6rem 0.7rem;
  background: var(--bg-panel);
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 0.6rem 1.6rem rgba(0, 0, 0, 0.45);
}

.view-row {
  display: contents;
}

.view-rule {
  flex: 1 0 100%;
  height: 0;
  margin: 0.15rem 0;
  border: 0;
  border-top: var(--hairline) solid var(--line);
}

.view-kinds {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

/* A button that stays pressed, so the state is visible rather than something
   to read off a checkbox beside a word. */
.btn.toggle[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #0a0d12;
}

.btn.toggle[aria-pressed="false"] {
  color: var(--text-dim);
}

/* A tick on whatever is on, so the state is not carried by color alone. Two
   borders on a rotated box rather than a character: a glyph brings its own
   baseline and its own idea of how much room to leave beside itself, and a
   check mark is not in every font a browser might fall back to. */
.btn.toggle[aria-pressed="true"]::before {
  content: "";
  display: inline-block;
  width: 0.3em;
  height: 0.55em;
  margin-right: 0.4em;
  border: solid currentColor;
  border-width: 0 0.14em 0.14em 0;
  transform: translateY(-0.08em) rotate(45deg);
}

/* The switch in the control column of a settings row. Square, because the
   name of the setting is already beside it, and the same height as the number
   boxes and sliders it lines up with. */
.btn.toggle.check {
  width: 1.6em;
  height: 1.6em;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.btn.toggle.check[aria-pressed="true"]::before {
  margin-right: 0;
}

/* The key list in the sprite editor. Folded away by default: a reference, not
   a control. The head itself folds like any other group's. */
.group.keys .stat {
  gap: 0.5rem;
}

kbd.key {
  min-width: 3.2em;
  padding: 0.05rem 0.3rem;
  border: var(--hairline) solid var(--line);
  border-radius: 0.25rem;
  background: var(--bg-raised);
  color: var(--text);
  font: inherit;
  font-size: 0.72rem;
  text-align: center;
}

/* ---- a rebuild waiting --------------------------------------------------
   A setting the running world was built from does not rebuild it as it is
   dragged. It is starred here and waits. */

.restart-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border-bottom: var(--hairline) solid var(--line);
  background: #2a2416;
  font-size: 0.78rem;
  flex: 0 0 auto;
}

.restart-bar[hidden] {
  display: none;
}

.restart-what {
  flex: 1 1 8rem;
  color: var(--accent-warm);
}

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

/* The star on a field the running world does not have yet. */
.field.waiting > .field-label::after {
  content: " *";
  color: var(--accent-warm);
  font-weight: 700;
}

.confirm {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gap);
  background: #05080cc0;
}

.confirm[hidden] {
  display: none;
}

.confirm-body {
  max-width: 26rem;
  padding: 1rem;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-panel);
  box-shadow: 0 1rem 2.5rem #0009;
}

.confirm-body h3 {
  margin: 0 0 0.4rem;
  font-size: 0.95rem;
}

.confirm-body .note {
  margin: 0 0 0.8rem;
}

/* A sheet a motion took before it was last drawn on. */
.field-hint.stale {
  color: var(--accent-warm);
}

/* Where a dragged frame or layer would land. */
[data-drag-at] {
  cursor: grab;
}

.drop-here {
  outline: 0.125rem solid var(--accent);
  outline-offset: -0.125rem;
}

/* What the marquee covers, and the two things to do with it. */
.marquee-row {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

/* One thing that can be given a picture instead of being generated. */
.made-slot {
  display: grid;
  grid-template-columns: 40% 1fr;
  align-items: center;
  gap: 0.4rem;
  padding: 0.2rem 0;
}

.made-slot .btn.danger {
  grid-column: 2;
  justify-self: start;
}

/* A filled slot says what the picture is and how large it comes out on the
   map, and carries the one number that changes it. Both belong under the drop
   zone rather than beside the label, which the first column is. */
.made-slot > .field-hint,
.made-slot > .field {
  grid-column: 2;
}

.made-slot > .field {
  grid-template-columns: 30% 1fr;
}

.made-group > summary {
  cursor: pointer;
  list-style: none;
}

.made-group > summary::-webkit-details-marker {
  display: none;
}

/* The search box over the picture slots, and its two switches. */
.made-search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0;
}

.made-search input[type="search"] {
  flex: 1 1 8rem;
  min-width: 0;
  padding: 0.25rem 0.4rem;
  border: var(--hairline) solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  color: var(--text);
  font: inherit;
  font-size: 0.8rem;
}

.made-slot .field-label {
  display: flex;
  flex-direction: column;
}

/* Every ease above is decoration over state that changes instantly; a reader
   who asked for still pages gets the states without the travel. */
@media (prefers-reduced-motion: reduce) {
  .layout,
  .panel,
  details.group::details-content,
  .view-menu::details-content {
    transition: none;
  }
}
