/* ------------------------------------------------------------------
   SPRAWL: layout for the page chrome only. Tokens, the chamfer, buttons,
   the logo mark, scanlines and the pulse dot come from ../core.css. The
   scene behind it is solid voxels (SPRAWL-SPEC.md section 1); the chrome in
   front of it follows STYLE-GUIDE.md with no exceptions: no border-radius
   (the status dot aside), chamfers cut top-right and bottom-left, volt leads,
   mono for anything the machine says, stepped motion, black on neon fills.

   Roles, since there are three neons on screen at once:
     volt  the player and what the player does: the clock, the bars, the
           action button, found terminals, "ROOT"
     hot   attention: the signal bar while hunting, tense talk, terminals
     cyan  information: tags, system lines, the friendly tone
   ------------------------------------------------------------------ */

body.sprawl {
  --pad: clamp(14px, 3vw, 32px);
  --bottom: var(--pad);             /* gap under the bottom-left and bottom-right panels */
  --top: 72px;                      /* first row under the 60px top bar */
  --map: 180px;                     /* minimap canvas, CSS px */
  --side-w: calc(var(--map) + 12px);
  /* Height of the top-right column (map, view row, seed form). The ID card
     sits under it, so it needs to know. map + 12 frame + 8 + 28 + 8 + 62. */
  --side-h: calc(var(--map) + 118px);

  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--screen);
  overscroll-behavior: none;
  /* No page scroll or pinch-zoom of the page itself; the renderer owns
     pinch on the canvas. Scrolling panels re-enable pan-y below. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

.stage { position: fixed; inset: 0; background: var(--screen); }
.stage canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }

kbd {
  display: inline-block;
  min-width: 22px; padding: 4px 6px; margin: 0 2px 0 0;
  border: 1px solid var(--line-2);
  font: 500 11px/1 var(--font-mono);
  text-align: center; text-transform: uppercase;
  color: var(--text);
  background: var(--bg-2);
}

/* ---------- Labels ---------- */
.sp-label {
  margin: 0;
  font: 500 11px/1 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted);
}

/* ---------- Top bar ---------- */
.sp-top {
  position: fixed; top: 0; left: 0; right: 0; z-index: 70;
  height: 60px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 0 clamp(14px, 3vw, 32px);
  font: 500 12px/1 var(--font-mono);
  letter-spacing: .08em; text-transform: uppercase;
  pointer-events: none;
}
.sp-top > * { pointer-events: auto; }
/* The brand name and the seed tag sit straight on the city, which has lit
   windows anywhere: they get the toggles' backing so they stay readable. */
.sp-top .brand-name { padding: 9px 10px; background: rgba(6, 7, 11, .86); }
.sp-seedtag {
  flex: 0 1 auto; margin: 0 auto;
  padding: 9px 12px;
  background: rgba(6, 7, 11, .86);
  text-align: center;
  font-size: 11px; letter-spacing: .16em;
  color: var(--muted);
  pointer-events: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sp-seedtag b { font-weight: 700; color: var(--text); }
/* The seed tag is the only item that gives way when a long seed is typed:
   it already ellipsises, while squeezed toggles would wrap their labels. */
.sp-toggles { display: flex; flex: none; gap: 8px; }
.sp-toggle {
  padding: 9px 11px;
  border: 1px solid var(--line-2);
  background: rgba(6, 7, 11, .86);
  color: var(--muted);
  font: inherit; letter-spacing: .12em; text-transform: uppercase;
  cursor: pointer;
  transition: color .12s, border-color .12s;
}
.sp-toggle:hover { color: var(--volt); border-color: var(--volt); }
/* Only the real settings dim when off. Help is a panel opener, never inactive,
   so it rests at --muted and lights while its panel is up. */
#btn-sound[aria-pressed="false"], #btn-scan[aria-pressed="false"] { color: var(--dim); }
#btn-help[aria-pressed="true"] { color: var(--volt); border-color: var(--volt); }

/* ---------- Status panel, top left ---------- */
.sp-status {
  position: fixed; top: var(--top); left: var(--pad); z-index: 60;
  width: 216px;
  padding: 16px 18px 12px;
  pointer-events: none;
}
.sp-clock {
  display: block; margin: 9px 0 6px;
  font: 700 50px/1 var(--font-display);
  letter-spacing: .02em;
  color: var(--volt);
  font-variant-numeric: tabular-nums;
}
.sp-rows { margin: 0; }
.sp-row {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 12px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--line-2);
  font: 500 11px/1 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase;
}
.sp-row:last-child { border-bottom: 0; padding-bottom: 4px; }
.sp-row dt { color: var(--muted); }
.sp-row dd {
  margin: 0; min-width: 0;
  color: var(--text); text-align: right;
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sp-row--found.is-root dd { color: var(--volt); }

/* The segment bar. The fill is a repeating 6px-on, 2px-off pattern that is
   cut to a width, so a partly full bar ends in a partial segment and the
   segments never stretch. */
.sp-seg { height: 8px; background: var(--line); overflow: hidden; }
.sp-row--signal .sp-seg { flex: 1 0 100%; }
.sp-seg i {
  display: block; height: 100%; width: 0;
  background: repeating-linear-gradient(90deg, var(--volt) 0 6px, transparent 6px 8px);
  transition: width .2s steps(4);
}
.sp-row--signal .sp-seg i { background: repeating-linear-gradient(90deg, var(--hot) 0 6px, transparent 6px 8px); }
/* All found: the row stops being about hunting. */
.sp-row--signal.is-root dd { color: var(--volt); font-weight: 700; }
.sp-row--signal.is-root .sp-seg i { background: repeating-linear-gradient(90deg, var(--volt) 0 6px, transparent 6px 8px); }
/* Close to a terminal: the number reads hot too. Hot text is 12px and up
   (STYLE-GUIDE contrast table), and the size is set for the row, not for
   the state, so the panel does not change height when the number turns hot. */
.sp-row--signal dd { font-size: 12px; }
.sp-row--signal.is-hot dd { color: var(--hot); font-weight: 700; }

/* ---------- Top right: minimap, view, seed ---------- */
.sp-side {
  position: fixed; top: var(--top); right: var(--pad); z-index: 60;
  width: var(--side-w);
  display: flex; flex-direction: column; gap: 8px;
}
.sp-map { --c: 16px; padding: 6px; pointer-events: none; }
.sp-map canvas { display: block; width: var(--map); height: var(--map); background: var(--screen); }

.sp-btn {
  padding: 0 8px; height: 28px;
  border: 1px solid var(--line-2);
  background: rgba(6, 7, 11, .86);
  color: var(--muted);
  font: 700 10px/1 var(--font-mono);
  letter-spacing: .12em; text-transform: uppercase;
  cursor: pointer;
  transition: color .12s, border-color .12s, background .12s;
}
.sp-btn:hover { color: var(--volt); border-color: var(--volt); }
.sp-btn:active { background: var(--volt); color: #000; }
.sp-btn--seg[aria-pressed="true"] { background: var(--volt); border-color: var(--volt); color: #000; }
.sp-btn--go { color: var(--volt); border-color: var(--volt); }
.sp-btn--go:hover { background: var(--volt); color: #000; }
.sp-btn--zoom { padding: 0; font-size: 14px; letter-spacing: 0; }

.sp-view { display: grid; grid-template-columns: 1fr 1fr 28px 28px; gap: 4px; }

.sp-seed { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.sp-seed input {
  grid-column: 1 / -1;
  height: 30px; min-width: 0; padding: 0 10px;
  border: 1px solid var(--line-2);
  background: var(--screen);
  color: var(--text);
  font: 500 12px/1 var(--font-mono);
  letter-spacing: .1em;
  caret-color: var(--volt);
  user-select: text; -webkit-user-select: text;
  transition: border-color .12s;
}
.sp-seed input::placeholder { color: var(--muted); }
.sp-seed input:focus { outline: 0; border-color: var(--volt); }
.sp-seed input:focus-visible { outline: 0; border-color: var(--volt); }
.sp-seed input.shake { border-color: var(--hot); }

/* ---------- ID card, bottom right ---------- */
.sp-card {
  position: fixed; right: var(--pad); bottom: var(--bottom); z-index: 60;
  width: 300px;
  max-height: calc(100vh - var(--top) - var(--side-h) - 8px - var(--bottom));
  max-height: calc(100dvh - var(--top) - var(--side-h) - 8px - var(--bottom));
  display: flex; flex-direction: column;
}
/* Fill mode is `backwards` on every animation that clips a chamfered element
   (card, readout, toast, bubble): `both` would hold the last keyframe's
   inset(0) after the entrance and override the chamfer polygon for good. */
.sp-card.pop, .sp-term.pop { animation: card-in .26s steps(4) backwards; }
@keyframes card-in { from { opacity: 0; clip-path: inset(0 0 100% 0); } to { opacity: 1; clip-path: inset(0 0 0 0); } }
/* The scroller is inside the chamfer, not the chamfer itself: ::before is
   absolutely positioned and would stay behind when the content scrolls. */
.sp-card-in {
  padding: 14px 18px 18px 18px;
  overflow-y: auto; min-height: 0;
  touch-action: pan-y;
  scrollbar-width: thin; scrollbar-color: var(--line-2) transparent;
  user-select: text; -webkit-user-select: text;
  /* A grid so the small-screen layout can move the Follow button up beside
     the name without a second copy of the markup. */
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-content: start;
  grid-template-areas: "head head" "name name" "line line" "quirk quirk" "tags tags" "mood mood" "act act" "meters meters" "follow follow";
}
.sp-card-head { grid-area: head; }
.sp-card-name { grid-area: name; }
.sp-card-line { grid-area: line; }
.sp-card-quirk { grid-area: quirk; }
.sp-tags { grid-area: tags; }
.sp-card-mood { grid-area: mood; }
.sp-card-act { grid-area: act; }
.sp-meters { grid-area: meters; }
.sp-follow { grid-area: follow; }
.sp-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.sp-card-head .sp-label span + span::before { content: ' // '; }
.sp-card-state { color: var(--cyan); }
.sp-card-state:empty { display: none; }
.sp-x {
  width: 26px; height: 26px; margin: -4px -4px -4px 0;
  border: 1px solid var(--line-2); background: var(--bg-2);
  color: var(--muted);
  font: 500 16px/1 var(--font-mono);
  cursor: pointer;
  transition: color .12s, border-color .12s, background .12s;
}
.sp-x:hover { background: var(--volt); border-color: var(--volt); color: #000; }
.sp-card-name {
  margin: 0 0 8px;
  font: 700 28px/1 var(--font-display);
  letter-spacing: -.01em; text-transform: uppercase;
  color: var(--text);
  overflow-wrap: anywhere;
}
.sp-card-line {
  margin: 0 0 10px;
  font: 500 11px/1.55 var(--font-mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
}
.sp-card-quirk {
  margin: 0 0 12px; padding-left: 10px;
  border-left: 2px solid var(--line-2);
  font: 400 14px/1.45 var(--font-display);
  color: var(--text);
}
.sp-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.sp-tags span {
  font: 500 10px/1 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--cyan);
  border: 1px solid rgba(34, 227, 238, .35);
  padding: 5px 8px;
}
.sp-card-mood { margin: 0 0 3px; font: 500 11px/1.5 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; }
.sp-card-mood b { font-weight: 700; color: var(--volt); }
.sp-card-act { margin: 0 0 12px; font: 500 11px/1.5 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.sp-meters { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; margin-bottom: 14px; }
.sp-meter span {
  display: flex; justify-content: space-between; margin-bottom: 6px;
  font: 500 10px/1 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
}
.sp-meter b { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.sp-meter.is-low i { background: repeating-linear-gradient(90deg, var(--hot) 0 6px, transparent 6px 8px); }
.sp-follow { width: 100%; justify-content: space-between; padding: 13px 16px; }

/* ---------- Town Talk, bottom left ---------- */
.sp-feed {
  position: fixed; left: var(--pad); bottom: var(--bottom); z-index: 60;
  width: min(340px, calc(100vw - 2 * var(--pad)));
  display: flex; flex-direction: column;
}
.sp-feed-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 22px 10px 16px;
  border-bottom: 1px solid var(--line-2);
  cursor: pointer;
}
.sp-feed-head .sp-label { display: flex; align-items: center; gap: 9px; }
.sp-feed-head .sp-label b { margin-left: 2px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.sp-feed-head .sp-label b.is-new { color: var(--volt); }
.sp-feed-toggle {
  padding: 5px 8px;
  border: 1px solid var(--line-2); background: var(--bg-2);
  color: var(--muted);
  font: 500 10px/1 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer;
  transition: color .12s, border-color .12s;
}
.sp-feed-toggle:hover { color: var(--volt); border-color: var(--volt); }
.sp-log {
  height: 224px;
  padding: 10px 14px 16px 16px;
  overflow-y: auto; overscroll-behavior: contain;
  touch-action: pan-y;
  scrollbar-width: thin; scrollbar-color: var(--line-2) transparent;
  user-select: text; -webkit-user-select: text;
}
.sp-feed.is-collapsed .sp-log { display: none; }
.sp-feed.is-collapsed .sp-feed-head { border-bottom: 0; }

.sp-ent {
  margin: 0 0 11px; padding: 0 0 0 10px;
  border-left: 2px solid var(--line-2);
  animation: ent-in .22s steps(4) both;
}
@keyframes ent-in { from { opacity: 0; clip-path: inset(0 100% 0 0); } to { opacity: 1; clip-path: inset(0 0 0 0); } }
.sp-ent-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px;
  margin: 0 0 3px;
  font: 500 10px/1.3 var(--font-mono);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted);
}
.sp-ent-head time { font-variant-numeric: tabular-nums; }
.sp-ent-names { color: var(--text); font-weight: 700; }
.sp-tone { padding: 2px 5px; border: 1px solid currentColor; }
.sp-tone--friendly { color: var(--cyan); }
/* Hot text is 12px and up and this tag is 10px, so the tension is in the border. */
.sp-tone--tense { color: var(--text); border-color: var(--hot); }
.sp-tone--odd { color: var(--text); }
.sp-line { margin: 2px 0 0 8px; font: 400 13px/1.4 var(--font-display); color: var(--text); }
.sp-line b {
  margin-right: 7px;
  font: 700 10px/1 var(--font-mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
}
.sp-line--b b { color: var(--cyan); }

/* One line, said to nobody: colour is the only difference from talk, since
   this site has no italics. */
.sp-ent--thought { border-left-color: var(--line); font: 400 13px/1.4 var(--font-display); color: var(--muted); }
.sp-ent--thought time { margin-right: 8px; font: 500 10px/1 var(--font-mono); letter-spacing: .12em; }
.sp-ent--thought b { margin-right: 6px; font: 700 10px/1 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text); }
.sp-ent--hail { border-left-color: var(--volt); }
.sp-ent--hail .sp-ent-head b { color: var(--volt); }
.sp-ent--hail .sp-ent-head span { color: var(--text); font-weight: 700; }
.sp-ent--terminal { border-left-color: var(--hot); }
.sp-ent--terminal .sp-ent-head b { font-size: 12px; color: var(--hot); }
.sp-ent--terminal.is-root { border-left-color: var(--volt); }
.sp-ent--terminal.is-root .sp-ent-head b { color: var(--volt); }
.sp-ent--terminal p.sp-tline { margin: 1px 0 0 8px; font: 400 11px/1.55 var(--font-mono); color: var(--muted); }
.sp-ent--terminal p.sp-tnext { margin-top: 4px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase; }
.sp-tnext b { font-weight: 500; color: var(--cyan); }
.sp-ent--sys { border-left-color: var(--cyan); font: 500 11px/1.5 var(--font-mono); letter-spacing: .04em; color: var(--cyan); }
.sp-ent--sys time { margin-right: 8px; color: var(--muted); letter-spacing: .12em; }

/* ---------- Prompt chip ---------- */
.sp-prompt {
  /* .chamfer sets position: relative; this has to win. */
  position: fixed; left: 50%; bottom: 28px; z-index: 61;
  --c: 9px; --bd: var(--volt); --fill: var(--bg-2);
  transform: translateX(-50%);
  padding: 11px 16px;
  font: 700 12px/1 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase; white-space: nowrap;
  color: var(--volt);
  pointer-events: none;
  animation: chip-in .16s steps(2) both;
}
@keyframes chip-in { from { opacity: 0; } to { opacity: 1; } }

/* ---------- Touch controls (shown in the small-screen block below) ---------- */
.sp-touch { display: none; }
.sp-stick {
  position: fixed; left: 20px; bottom: 60px; z-index: 62;
  --c: 22px; width: 116px; height: 116px;
  background: var(--line-2);
  clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 22px 100%, 0 calc(100% - 22px));
  touch-action: none;
  cursor: grab;
}
.sp-stick::before {
  content: ''; position: absolute; inset: 1px;
  background: var(--bg-2);
  clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 22px 100%, 0 calc(100% - 22px));
}
/* Cross-hair: two 1px lines. Same colour, so not a gradient in any sense that matters. */
.sp-stick::after {
  content: ''; position: absolute; inset: 14px;
  background:
    linear-gradient(var(--line-2), var(--line-2)) center / 1px 100% no-repeat,
    linear-gradient(var(--line-2), var(--line-2)) center / 100% 1px no-repeat;
  pointer-events: none;
}
.sp-stick.is-held { background: var(--volt); }
.sp-knob {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  width: 46px; height: 46px; margin: -23px 0 0 -23px;
  background: var(--volt);
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 13px, 100% 100%, 13px 100%, 0 calc(100% - 13px));
  pointer-events: none;
  will-change: transform;
}

.sp-pad {
  position: fixed; right: 14px; bottom: 14px; z-index: 62;
  display: grid; grid-template-columns: 44px 44px 44px; gap: 6px;
  align-items: end; justify-items: end;
}
.sp-tbtn {
  width: 44px; height: 44px; padding: 0;
  border: 1px solid var(--line-2);
  background: rgba(6, 7, 11, .86);
  color: var(--text);
  font: 700 10px/1 var(--font-mono);
  letter-spacing: .12em; text-transform: uppercase;
  touch-action: none; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.sp-tbtn svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2.5; stroke-linejoin: miter; stroke-linecap: butt; }
.sp-tbtn[aria-pressed="true"], .sp-tbtn.held { background: var(--volt); border-color: var(--volt); color: #000; }
.sp-tbtn--run { grid-column: 1; grid-row: 2; width: 52px; height: 52px; }
.sp-tbtn--act {
  grid-column: 2 / span 2; grid-row: 2; width: 74px; height: 74px;
  /* .chamfer sets position: relative; keep it in the grid flow. */
  --bd: var(--volt); --fill: var(--volt);
  border: 0; color: #000;
  font-size: 12px; letter-spacing: .16em;
}
.sp-tbtn--act:focus-visible { outline: 2px solid #000; outline-offset: -8px; }
.sp-tbtn--act.held { --fill: #fff; background: var(--bd); }
.sp-tbtn--act.held::before { background: #fff; }

/* ---------- Centre panels ---------- */
.sp-wrap {
  position: fixed; inset: 60px 0 0 0; z-index: 65;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 16px var(--pad) 24px;
  overflow-y: auto;
  touch-action: pan-y;
  /* Transparent to the pointer around the panel: the city is still there. */
  pointer-events: none;
}
.sp-wrap--dim { background: rgba(3, 4, 7, .74); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); pointer-events: auto; }
.sp-wrap--error { z-index: 67; }
.sp-wrap > .chamfer { pointer-events: auto; margin-block: auto; }

.sp-head {
  margin: 0 0 16px;
  font: 700 clamp(2.1rem, 6vw, 3rem)/1 var(--font-display);
  text-transform: uppercase; letter-spacing: -.01em;
}
.sp-head--hot { color: var(--hot); }
.sp-blurb { margin: 0 0 22px; font-size: 15px; line-height: 1.55; color: var(--muted); }
.sp-blurb b { color: var(--text); font-weight: 600; }
.sp-hot { --bd: var(--hot); }

/* Help */
.sp-help {
  --c: 22px;
  width: 100%; max-width: 640px;
  padding: 28px 28px 26px;
  user-select: text; -webkit-user-select: text;
}
.sp-help--small { max-width: 380px; }
.sp-help-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-bottom: 26px; }
.sp-help-cols .sp-label { margin-bottom: 12px; }
.sp-keys { margin: 0; }
.sp-keys div {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--line-2);
}
.sp-keys dt { flex: none; white-space: nowrap; }
.sp-keys dd {
  margin: 0; text-align: right;
  font: 500 11px/1.4 var(--font-mono);
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}

/* Terminal readout: the .term look from the landing page, standing alone. */
.sp-term {
  --c: 22px; --bd: var(--volt); --fill: #07090e;
  width: 100%; max-width: 620px;
  font: 400 14px/1.7 var(--font-mono);
  user-select: text; -webkit-user-select: text;
}
.sp-term-bar {
  display: flex; align-items: center; gap: 14px;
  margin: 1px 1px 0;
  padding: 12px 22px 12px 18px;
  border-bottom: 1px solid var(--line-2);
  background: var(--panel);
  font-size: 12px; letter-spacing: .08em;
  color: var(--text);
}
.sp-term-dots { display: flex; gap: 6px; }
.sp-term-dots i { width: 9px; height: 9px; background: var(--line-2); }
.sp-term-dots i:first-child { background: var(--hot); }
.sp-term-dots i:nth-child(2) { background: var(--volt); }
.sp-term-state { margin-left: auto; font-size: 11px; color: var(--muted); text-transform: uppercase; }
.sp-term.is-root .sp-term-state { color: var(--volt); }
.sp-term-body { min-height: 150px; padding: 18px 22px 8px; cursor: default; }
.sp-term-body p { margin: 0; padding-left: 2ch; text-indent: -2ch; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--text); }
.sp-term-body p::before { content: '> '; color: var(--volt); }
/* A line that has not started typing keeps its place but shows no prompt. */
.sp-term-body p:empty { visibility: hidden; }
.sp-term-body p.t-hi { color: var(--cyan); }
.sp-term-body p.t-ok { color: var(--volt); }
.sp-term-body p.t-out { color: var(--muted); }
.sp-term-body .t-cursor { display: inline-block; width: .6em; height: 1em; margin-left: 2px; vertical-align: -.15em; background: var(--volt); animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.sp-term-hint { margin: 0; padding: 4px 22px 14px; font: 500 11px/1.5 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.sp-term-hint b { font-weight: 500; color: var(--cyan); }
.sp-term-foot { padding: 4px 22px 22px; }

/* ---------- Loading, toast ---------- */
.sp-load {
  position: fixed; inset: 0; z-index: 66;
  display: grid; place-items: center;
  background: var(--screen);
  padding: 0 var(--pad);
}
.sp-load-box { width: min(340px, 100%); }
.sp-load-title {
  margin: 0 0 16px;
  font: 700 14px/1.3 var(--font-mono);
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--text);
  overflow-wrap: anywhere;
}
.sp-load-title em { font-style: normal; color: var(--volt); }
.sp-load-bar { height: 8px; background: var(--line); overflow: hidden; }
/* 30 steps of 8px across a 240px-wide fill is one segment per step; the
   track is wider, so the fill stops at 240 and starts over. */
.sp-load-bar i {
  display: block; height: 100%; width: 40%;
  background: repeating-linear-gradient(90deg, var(--volt) 0 6px, transparent 6px 8px);
  animation: load-fill 1.5s steps(36) infinite;
}
@keyframes load-fill { from { width: 0; } to { width: 100%; } }
.sp-load-sub { margin: 14px 0 0; min-height: 1.2em; font: 500 11px/1.4 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }

.sp-toast {
  /* .chamfer sets position: relative; this has to win. */
  position: fixed; top: var(--top); left: 50%; z-index: 68;
  --c: 12px; --bd: var(--cyan); --fill: var(--bg-2);
  transform: translateX(-50%);
  max-width: calc(100vw - 2 * var(--pad));
  padding: 12px 20px;
  font: 700 12px/1.3 var(--font-mono);
  letter-spacing: .14em; text-transform: uppercase; text-align: center;
  color: var(--cyan);
  pointer-events: none;
  animation: toast-in .24s steps(4) backwards;
}
.sp-toast--ok { --bd: var(--volt); color: var(--volt); }
.sp-toast--hot { --bd: var(--hot); color: var(--hot); }
@keyframes toast-in { from { opacity: 0; clip-path: inset(0 50% 0 50%); } to { opacity: 1; clip-path: inset(0 0 0 0); } }

.sp-noscript {
  position: fixed; inset: 0; z-index: 99;
  display: grid; place-content: center; gap: 18px; justify-items: start;
  padding: var(--pad);
  background: var(--screen);
  font: 500 13px/1.6 var(--font-mono);
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
}
.sp-noscript p { margin: 0; max-width: 520px; }
.sp-noscript a { color: var(--volt); }

/* ---------- Speech bubbles ----------
   sprawl.js creates <div class="bubble"> (or "bubble bubble--thought") inside
   #bubbles and sets only `style.transform = translate(Xpx, Ypx)` to the
   head's screen position. The offset below puts the bubble above and
   centred on that point; `translate` is a separate property from `transform`,
   so the renderer's per-frame writes do not undo it. The chamfer cut at
   the bottom-left stands in for a tail. */
.sp-bubbles { position: fixed; inset: 0; z-index: 40; pointer-events: none; overflow: hidden; }
.bubble {
  --c: 9px; --bd: rgba(34, 227, 238, .55); --fill: #0b0d14;
  position: absolute; left: 0; top: 0;
  translate: -50% calc(-100% - 10px);
  width: max-content; max-width: 188px;
  padding: 7px 11px 8px;
  background: var(--bd);
  clip-path: polygon(0 0, calc(100% - var(--c)) 0, 100% var(--c), 100% 100%, var(--c) 100%, 0 calc(100% - var(--c)));
  font: 500 13px/1.35 var(--font-display);
  color: var(--text);
  isolation: isolate;
  will-change: transform;
  animation: bubble-in .18s steps(3) backwards;
}
.bubble::before {
  content: ''; position: absolute; inset: 1px; z-index: -1;
  background: var(--fill);
  clip-path: polygon(0 0, calc(100% - var(--c)) 0, 100% var(--c), 100% 100%, var(--c) 100%, 0 calc(100% - var(--c)));
}
/* A thought is said to nobody: dimmer border, muted text. */
.bubble--thought { --bd: rgba(122, 131, 155, .5); color: var(--muted); }
/* A reply to the player's poke. The renderer does not have to know about it. */
.bubble--hail { --bd: var(--volt); }
@keyframes bubble-in { from { opacity: 0; clip-path: inset(0 100% 0 0); } to { opacity: 1; clip-path: inset(0 0 0 0); } }

/* ---------- uidemo scaffolding ---------- */
.sp-demo-bg { position: absolute; inset: 0; width: 100%; height: 100%; }
.sp-demo-flag { color: var(--hot); font-size: 12px; }

/* ---------- Short desktop screens ---------- */
@media (max-height: 780px) {
  body.sprawl { --map: 144px; --bottom: 16px; }
  .sp-status { padding: 12px 16px 8px; }
  .sp-clock { font-size: 42px; margin: 7px 0 4px; }
  .sp-row { padding: 6px 0; }
  .sp-card-in { padding: 12px 16px 12px; }
  .sp-card-head { margin-bottom: 8px; }
  .sp-card-name { font-size: 22px; margin-bottom: 6px; }
  .sp-card-line { margin-bottom: 8px; }
  .sp-card-quirk { margin-bottom: 9px; font-size: 13px; }
  .sp-tags { margin-bottom: 9px; }
  .sp-card-act { margin-bottom: 9px; }
  .sp-meters { margin-bottom: 11px; gap: 8px 14px; }
  .sp-follow { padding: 11px 16px; }
  .sp-log { height: 190px; }
  .sp-help { padding: 22px 26px 20px; }
  .sp-help .sp-head { margin-bottom: 12px; }
  .sp-help .sp-blurb { margin-bottom: 14px; }
  .sp-help-cols { margin-bottom: 18px; }
  .sp-keys div { padding: 6px 0; }
}
@media (max-height: 720px) {
  body.sprawl { --map: 136px; }
}
@media (max-height: 640px) {
  .sp-log { height: 140px; }
  .sp-status .sp-row--cars { display: none; }
}

/* ---------- Small screens and touch ----------
   Touch (a coarse pointer) or under 1000px wide. The width rule is
   sufficient on its own: a headless or desktop browser narrowed past 1000
   gets the same layout. The feed collapses to its header (sprawl-ui.js
   does that at start-up), the status panel shrinks to clock, terminals
   and signal, and a stick and buttons appear. */
@media (max-width: 999px), (pointer: coarse) {
  .sp-touch { display: block; }
  .sp-status { width: 164px; padding: 12px 14px 8px; }
  .sp-clock { font-size: 38px; margin: 6px 0 2px; }
  .sp-row--pop, .sp-row--cars, .sp-row--district { display: none; }
  .sp-view { display: none; }
  .sp-btn { height: 30px; }
  .sp-prompt { bottom: 188px; }
  .sp-toast { top: auto; bottom: 232px; }
  body.sprawl { --bottom: 14px; }
  .sp-feed { width: auto; }
  .sp-feed.is-collapsed { width: auto; }
  .sp-feed.is-collapsed .sp-feed-head { gap: 10px; padding: 9px 16px 9px 14px; }
  /* Collapsed, the chip is just a header: the button shrinks to a plus. */
  .sp-feed.is-collapsed .sp-feed-toggle { font-size: 0; padding: 5px 7px; line-height: 1; }
  .sp-feed.is-collapsed .sp-feed-toggle::after { content: '+'; font: 700 12px/1 var(--font-mono); }
  /* Opened on purpose, it is a reading mode: it covers the stick and buttons
     (z-index above them) until it is hidden again. */
  .sp-feed:not(.is-collapsed) { width: calc(100vw - 2 * var(--pad)); z-index: 63; }
  /* It also covers the prompt chip's bottom edge, and the Act button the chip
     points at is under it: hide the chip while the feed is open (a class on
     body, set in sprawl-ui.js, since the feed is not the chip's parent). */
  body.feed-open .sp-prompt { display: none; }
  .sp-log { height: 150px; }

  /* The ID card takes the top of the screen and the HUD steps aside while it
     is up; the bottom half belongs to the stick and buttons. */
  .sp-card {
    left: 0; right: 0; bottom: auto; top: 66px;
    width: min(440px, calc(100vw - 2 * var(--pad))); margin: 0 auto;
    max-height: calc(100dvh - 66px - 190px);
  }
  body.has-card .sp-status, body.has-card .sp-side { visibility: hidden; }
  .sp-card-in {
    padding: 10px 14px 12px; column-gap: 10px;
    grid-template-areas: "head head" "name follow" "line line" "quirk quirk" "tags mood" "act act" "meters meters";
  }
  .sp-card-head { margin-bottom: 4px; }
  .sp-card-name { font-size: 22px; margin: 0 0 4px; align-self: center; }
  .sp-card-line { margin-bottom: 6px; line-height: 1.45; }
  .sp-card-quirk { margin-bottom: 8px; font-size: 13px; line-height: 1.35; }
  .sp-tags { margin-bottom: 6px; align-self: center; }
  .sp-card-mood { margin: 0 0 6px; align-self: center; text-align: right; }
  .sp-card-act { margin-bottom: 8px; }
  .sp-meters { grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 0; }
  .sp-meter span { flex-direction: column; gap: 4px; margin-bottom: 5px; }
  .sp-follow { width: auto; padding: 9px 12px; gap: 10px; margin-bottom: 4px; }
  .sp-help-cols { grid-template-columns: 1fr; gap: 16px; }
  /* A tall panel on a short screen: the way out stays in reach. */
  .sp-help > .btn { position: sticky; bottom: 4px; }
}
/* On a touch screen the touch controls come first; the keys are for the
   keyboard that is probably not there. */
@media (pointer: coarse) {
  .sp-help-cols > div:last-child { order: -1; }
}

@media (max-width: 680px) {
  body.sprawl { --map: 120px; --side-h: calc(var(--map) + 12px + 8px + 30px + 4px + 30px + 4px + 30px); }
  .brand-name { display: none; }
  .sp-seedtag { display: none; }
  .sp-top { justify-content: space-between; }
  .sp-status { width: 158px; }
  .sp-row { letter-spacing: .1em; }
  .sp-seed { grid-template-columns: 1fr; }
  .sp-help { padding: 22px 20px; }
  .sp-term-body { padding: 14px 16px 6px; font-size: 13px; }
  .sp-term-bar { padding: 10px 18px 10px 14px; gap: 10px; }
  .sp-term-hint, .sp-term-foot { padding-left: 16px; padding-right: 16px; }
}

/* Narrow phones: less room beside the stick, so a smaller stick and buttons,
   and a collapsed feed that is only its name. */
@media (max-width: 360px) {
  .sp-stick { left: 14px; width: 100px; height: 100px; }
  .sp-pad { grid-template-columns: repeat(3, 40px); gap: 5px; right: 12px; }
  .sp-tbtn { width: 40px; height: 40px; }
  .sp-tbtn--run { width: 46px; height: 46px; }
  .sp-tbtn--act { width: 66px; height: 66px; }
  .sp-feed.is-collapsed .sp-feed-toggle { display: none; }
}

/* Landscape phones: very little height. Keep the stick and the essentials. */
@media (max-height: 480px) and (max-width: 999px) {
  .sp-top { height: 48px; }
  .sp-wrap { inset: 48px 0 0 0; }
  body.sprawl { --top: 56px; }
  .sp-map { display: none; }
  .sp-status { padding: 8px 12px 4px; }
  .sp-clock { font-size: 30px; margin: 4px 0 0; }
  .sp-stick { bottom: 18px; width: 96px; height: 96px; }
  /* Town Talk keeps its header chip, under the status panel (about 120px tall
     here), and opens in place, narrow enough to leave the middle of the screen
     to the avatar. Anchored from the top: the bottom left is the stick's. */
  .sp-feed, .sp-feed.is-collapsed { top: calc(var(--top) + 128px); bottom: auto; }
  .sp-feed:not(.is-collapsed) { width: min(280px, calc(100vw - 2 * var(--pad))); }
  .sp-log { height: 100px; }
  .sp-prompt { bottom: 20px; }
  .sp-pad { bottom: 10px; }
  /* Right of centre, so the avatar (the middle of the screen) stays in view,
     and above the touch pad: its buttons are 124px tall on a 10px offset, and
     the card scrolls inside what is left. */
  .sp-card { left: auto; right: var(--pad); top: 54px; width: 300px; margin: 0; max-height: calc(100dvh - 54px - 142px); }
}
/* Under about 340px of height the status panel, the chip and the stick no
   longer fit one above another, so the stick wins and Town Talk drops out. */
@media (max-height: 340px) and (max-width: 999px) {
  .sp-feed { display: none; }
}
