/* One sheet for every page. Separation is by plane, never by a border: the
   only rings are focus and a danger ring on an input that can't be used.
   Colour literals live in :root and nowhere else. */

@font-face {
  font-family: "Geist";
  src: url("/fonts/Geist-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("/fonts/Geist-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/GeistMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/GeistMono-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}

:root {
  --page: oklch(0.15 0.012 293);
  --surface: oklch(0.19 0.014 293);
  --raised: oklch(0.23 0.016 293);
  --raised-hi: oklch(0.27 0.018 293);
  --ink: oklch(0.94 0.006 293);
  --mu: oklch(0.72 0.014 293);
  /* Ink for the mark on an empty screen: present, and quieter than text. */
  --dim: oklch(0.4 0.016 293);
  --accent: oklch(0.71 0.16 293);
  --accent-ink: oklch(0.17 0.03 293);
  --accent-tint: oklch(0.71 0.16 293 / 0.16);
  --ok: oklch(0.8 0.15 150);
  --ok-fill: oklch(0.27 0.05 150);
  --warn: oklch(0.83 0.14 80);
  --warn-fill: oklch(0.27 0.05 80);
  --danger: oklch(0.72 0.17 25);
  --danger-fill: oklch(0.27 0.06 25);
  --neutral-fill: oklch(0.27 0.016 293);
  /* Below the page: the slot a dragged tile leaves behind, and a choice
     that cannot be taken. */
  --sunk: oklch(0.17 0.013 293);
  --slot-ring: oklch(0.3 0.02 293);
  /* What a lifted tile casts. */
  --lift: oklch(0.06 0.01 293 / 0.7);
  /* A person's own tile carries more of the accent than any plane does, so
     a face is never read as one more surface behind the page. */
  --person: oklch(0.27 0.04 293);
  /* The tints every picture is drawn in: the three a computer uses, plus a
     roof, foliage and glass, which only a house has. */
  --pic-body: oklch(0.34 0.022 293);
  --pic-inset: oklch(0.26 0.018 293);
  --pic-line: oklch(0.52 0.03 293);
  --pic-roof: oklch(0.42 0.06 30);
  --pic-leaf: oklch(0.42 0.07 150);
  --pic-glass: oklch(0.46 0.06 230);
  --backdrop: oklch(0.1 0.01 293 / 0.7);
  --shadow: oklch(0 0 0 / 0.5);
  --mono: "Geist Mono", ui-monospace, monospace;
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--page);
  color: var(--ink);
  font: 400 14px/1.5 "Geist", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* The agent column is as wide as its edge was dragged (agent.js), and never
   so wide that the main column drops under 480px. */
body {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr) clamp(280px, var(--agent-w, 400px), calc(100vw - 700px));
  overflow: hidden;
}

::selection { background: var(--accent); color: var(--accent-ink); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
::placeholder { color: var(--mu); opacity: 1; }

v-icon { display: inline-flex; flex: none; width: 16px; height: 16px; }

.mono { font-family: var(--mono); }
.muted { color: var(--mu); }
.ok-ink { color: var(--ok); }
.warn-ink { color: var(--warn); }
.danger-ink { color: var(--danger); }
.spin { animation: spin 1s linear infinite; }
.loading { display: flex; align-items: center; gap: 8px; }
.loading v-icon { width: 16px; height: 16px; }

/* --- sidebar ----------------------------------------------------------- */

/* Every row inside adds 10px of its own, so the house picture, the nav
   icons and the avatar all start on one x. */
.side {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
  padding: 12px;
  background: var(--surface);
}

.me { position: relative; }
/* Y1: the house's own screens, then the line, then the person's. The free
   space of a short sidebar falls above the line, never under it. */
.side-rule { margin-top: auto; height: 1px; background: var(--raised-hi); }

/* The switcher and the avatar are the same row: a tile, a slot, a chevron. */
.switch {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 48px;
  padding: 0 10px;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--ink);
  cursor: pointer;
}
.switch:hover { background: var(--raised); }
.switch-tile {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 2px;
  background: var(--raised);
  color: var(--ink);
  font-weight: 600;
  object-fit: cover;
}
.switch-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  font-weight: 600;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Q2: picture and name on one centre line, whether the name takes one line
   or two. */
.switch-house {
  align-items: center;
  gap: 10px;
  height: auto;
  min-height: 48px;
  padding: 6px 10px;
}
.switch-house .switch-name {
  display: -webkit-box;
  overflow: hidden;
  line-height: 1.3;
  white-space: normal;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
/* The person's row is not the infrastructure's: a username is a machine
   value, and it never competes with the switcher above it. */
.me-name { font-weight: 400; color: var(--mu); }

.nav { display: flex; flex-direction: column; gap: 4px; }
.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--mu);
  text-align: left;
  cursor: pointer;
}
.nav-item v-icon { width: 18px; height: 18px; }
.nav-item:hover { background: var(--raised); }
.nav-item.current { background: var(--raised); color: var(--ink); font-weight: 600; }
.soon { opacity: 0.4; cursor: not-allowed; }

.menu {
  position: absolute;
  left: 0;
  z-index: 5;
  min-width: 100%;
  width: max-content;
  max-width: 240px;
  padding: 4px;
  border-radius: 2px;
  background: var(--raised);
  box-shadow: 0 12px 32px var(--shadow);
}
.menu-down { top: 52px; }
.menu-anchor { position: relative; }
.menu-end { top: 40px; right: 0; left: auto; min-width: 160px; }
.menu-up { bottom: 52px; }
.menu form { margin: 0; }
.menu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 36px;
  padding: 0 8px;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--ink);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.menu-item:hover { background: var(--raised-hi); }
.menu-item.soon:hover { background: none; }
.menu-down .menu-item { gap: 8px; }
.menu-item.current { background: var(--accent-tint); }
.menu-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-rule { height: 1px; margin: 4px 0; background: var(--raised-hi); }
/* A switch is on or off and nothing between, so the knob's side says which
   without reading the label (N4, B7). */
.menu-switch { gap: 12px; height: 40px; }
.menu-switch:hover { background: none; }
.menu-chat { width: 240px; }
.sw {
  position: relative;
  flex: none;
  width: 36px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: var(--raised-hi);
  cursor: pointer;
}
.sw[aria-checked="true"] { background: var(--accent); }
.sw-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--ink);
}
.sw[aria-checked="true"] .sw-knob { left: 18px; background: var(--accent-ink); }
.dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--mu); }
.dot-ok { background: var(--ok); }
.dot-danger { background: var(--danger); }

/* --- main column ------------------------------------------------------- */

.main { display: flex; flex-direction: column; min-width: 0; overflow: auto; }
/* One container for every page, left-aligned against the sidebar: every
   page's left edge and its title row sit in the same place, so navigating
   never moves them, and the space a wide screen has left over stays empty
   on the right (B6). Wide holds grids and lists, narrow forms and reading.
   The 24px gutters sit outside the column those numbers name. */
.view { display: flex; flex: 1; flex-direction: column; padding: 24px; max-width: calc(1120px + 48px); }
.view.narrow { max-width: calc(640px + 48px); }

.attention { display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: var(--surface); }
/* The rows above the page belong to it, so they end where it ends. */
#attention { max-width: calc(1120px + 48px); }
#attention > .attention { margin: 24px 24px 0; }
/* A row sits one plane above what it rests on, so in the column it is raised. */
#agent-notice > .attention { margin: 0 16px 8px; background: var(--raised); }

.screen { display: flex; flex: 1; flex-direction: column; gap: 24px; }
.screen-head { display: flex; align-items: center; gap: 12px; }
.screen-title { margin: 0; font-size: 22px; font-weight: 600; }
.head-action { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.empty { margin: 0; padding: 16px; border-radius: 2px; background: var(--surface); color: var(--mu); }
/* An empty screen: one mark and one way on, centred in the space the list
   would have taken. */
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  min-height: 360px;
  padding: 48px 16px;
}
.hero-icon { width: 96px; height: 96px; color: var(--dim); }
/* Why an empty infrastructure is still here, in the hero's own rhythm. */
.hero-err { margin: 0; text-align: center; }

/* A spec list: one technical fact per row, the label muted and the value in
   Geist Mono. Inside a card the rows take the plane under it, so the card
   still reads as one surface. */
.specs { display: flex; flex-direction: column; gap: 2px; }
.spec {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 12px;
  padding: 6px 12px;
  border-radius: 2px;
  background: var(--surface);
}
.spec-label { color: var(--mu); }
.card .spec { background: var(--page); }

/* --- buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: 2px;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { filter: brightness(1.1); }
.btn-primary { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.btn-secondary { background: var(--raised); color: var(--ink); }
.btn-secondary:hover { background: var(--raised-hi); filter: none; }
.btn-danger { background: var(--danger); color: var(--accent-ink); font-weight: 600; }
.btn[disabled] { background: var(--raised); color: var(--mu); cursor: not-allowed; filter: none; }

.icon-btn {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--mu);
  cursor: pointer;
}
.icon-btn:hover { background: var(--raised); color: var(--ink); }
.icon-btn-lg { width: 36px; height: 36px; }

.info { display: inline-flex; color: var(--mu); cursor: help; }
.info v-icon, .modal-head v-icon { width: 18px; height: 18px; }

/* --- status pills ------------------------------------------------------ */

.pill {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 8px;
  border-radius: 2px;
  white-space: nowrap;
}
.pill-ok { background: var(--ok-fill); color: var(--ok); }
.pill-warn { background: var(--warn-fill); color: var(--warn); }
.pill-danger { background: var(--danger-fill); color: var(--danger); }
.pill-neutral { background: var(--neutral-fill); color: var(--mu); }

/* --- the computers grid ------------------------------------------------- */

/* Tiles of one size that wrap, so a fleet of three and a fleet of twelve are
   the same screen. */
.grid { display: grid; grid-template-columns: repeat(auto-fill, 168px); gap: 8px; }
.ctile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 168px;
  padding: 20px 12px 16px;
  border: 0;
  border-radius: 2px;
  background: var(--surface);
  color: var(--ink);
  text-align: center;
  cursor: pointer;
  /* The pointer drags the tile; it never scrolls the page off it. */
  touch-action: none;
}
.ctile:hover { background: var(--raised); }
.ctile > * { max-width: 100%; }
.ctile-name { font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctile-spec { font-size: 13px; color: var(--mu); text-wrap: pretty; }
/* One square per block: the tile's last row is read at a glance, not
   counted. It keeps its height on a computer with nothing to show. */
.ind, .ind-gap { height: 16px; }
.ind { display: flex; align-items: center; gap: 12px; }
.ind-blocks { display: flex; align-items: center; gap: 3px; height: 8px; }
.ind-block, .ind-none { width: 8px; height: 8px; border-radius: 1px; }
.ind-block { background: var(--accent); }
.ind-none { box-shadow: inset 0 0 0 1.5px var(--pic-body); }
/* CPU and disk beside the squares: how full each is, with the exact number
   in the tooltip. A meter turns amber at 85%, where it stops being
   information and starts being something to see to. */
.ind-meters { display: flex; gap: 4px; }
.ind-meter { display: block; width: 44px; height: 4px; border-radius: 2px; background: var(--raised-hi); }
.ind-fill { display: block; height: 4px; border-radius: 2px; background: var(--accent); }
.ind-fill.ind-high { background: var(--warn); }
/* A tile dragged to a new place lifts out of the grid and follows the
   pointer; a dark slot holds where it lands. */
.ctile-lift {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  background: var(--surface);
  box-shadow: 0 16px 36px var(--lift);
  transform: rotate(2deg) scale(1.03);
}
.ctile-slot { width: 168px; border-radius: 2px; background: var(--sunk); box-shadow: inset 0 0 0 2px var(--slot-ring); }

/* --- the pictures ------------------------------------------------------- */

.pic { flex: none; }
.pic-body { fill: var(--pic-body); }
.pic-inset { fill: var(--pic-inset); }
.pic-edge { fill: var(--pic-line); }
.pic-light { fill: var(--accent); }
.pic-line { fill: none; stroke: var(--pic-line); stroke-width: 1.5; stroke-linecap: round; }
.pic-pins { stroke-width: 2; }
.pic-ghost { fill: none; stroke: var(--pic-line); stroke-width: 1.5; stroke-dasharray: 4 3; }
/* A house is drawn in the same box and tints: a roof, warm windows and the
   violet light, plus the few parts only one house has. */
.pic-roof { fill: var(--pic-roof); }
.pic-leaf { fill: var(--pic-leaf); }
.pic-window { fill: var(--warn); }
.pic-cap { fill: var(--accent); }
.pic-trunk { fill: var(--pic-line); }
.pic-band { fill: none; stroke: var(--pic-roof); stroke-width: 5; }
.pic-seam { fill: none; stroke: var(--pic-inset); stroke-width: 1.5; }
.pic-rung { fill: none; stroke: var(--pic-line); stroke-width: 1; }
.pic-beam { fill: var(--warn); opacity: 0.35; }
.pic-sail { fill: var(--mu); opacity: 0.5; }
.pic-plus { fill: none; stroke: var(--pic-line); stroke-width: 2; stroke-linecap: round; }
.pic-glass { fill: var(--pic-glass); }
.pic-post { fill: none; stroke: var(--pic-body); stroke-width: 2; }
.pic-glint { fill: none; stroke: var(--warn); stroke-width: 1.5; }
.pic-wheel { fill: var(--pic-inset); stroke: var(--pic-line); stroke-width: 1.5; }
.pic-gear { fill: none; stroke: var(--accent); stroke-width: 2; }
/* On a computer's page the picture is a control, and the only one there that
   changes nothing about the computer. */
.pic-btn { display: inline-flex; padding: 0; border: 0; background: none; cursor: pointer; }
.pic-head { align-items: center; gap: 16px; }
.pgrid { display: grid; grid-template-columns: repeat(auto-fill, 112px); gap: 8px; }
/* A house's pictures come in three rows, each under what it is for (Q7). */
.pgroups { display: flex; flex-direction: column; gap: 20px; }
.pgroup { display: flex; flex-direction: column; gap: 8px; }
.pcell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 112px;
  padding: 14px 8px 10px;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--mu);
  cursor: pointer;
}
.pcell:hover { background: var(--raised-hi); }
.pcell.on { box-shadow: inset 0 0 0 2px var(--accent); }
.pcell.on .pcell-name { color: var(--ink); }
.pcell-pick { font-size: 13px; }

/* --- one computer ------------------------------------------------------ */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.tile { display: flex; flex-direction: column; gap: 4px; padding: 16px; border-radius: 2px; background: var(--surface); }
.tile-label { color: var(--mu); }

.block { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; padding: 24px; border-radius: 2px; background: var(--surface); }
.block > p { margin: 0; }
.steps { display: flex; flex-direction: column; gap: 8px; }
.step { display: flex; align-items: center; gap: 12px; }
.step-later .step-label { color: var(--mu); }
.step-gap { flex: none; width: 16px; height: 16px; }
.step-trail { color: var(--mu); }

.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.note { margin: 0; color: var(--mu); }

/* --- blocks ------------------------------------------------------------- */

/* The store's search field, the only full-width one: the icon sits inside
   it. Searching what this fleet runs opens from the muted icon that ends
   the header row, into a field in that row's place. */
.search {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 12px;
  border-radius: 2px;
  background: var(--raised);
}
.search:has(.search-input:focus),
.find-field:has(.search-input:focus) { box-shadow: inset 0 0 0 2px var(--accent); }
.search-icon { flex: none; width: 16px; height: 16px; color: var(--mu); }
.search-input { flex: 1; min-width: 0; height: 36px; border: 0; background: none; color: inherit; font: inherit; }
.search-input:focus { outline: none; }
.find-btn { width: 28px; height: 28px; justify-self: end; margin-right: -6px; }
.find-field {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 4px 0 12px;
  border-radius: 2px;
  background: var(--raised);
}
.find-field .search-input { height: 32px; }
.find-close { width: 28px; height: 28px; }
.find-close v-icon { width: 14px; height: 14px; }

/* A list of rows: the header row, then rows one plane above the page. */
.table { display: flex; flex-direction: column; gap: 2px; }
.thead { padding: 0 16px 4px; color: var(--mu); }
.trow {
  width: 100%;
  padding: 12px 16px;
  border: 0;
  border-radius: 2px;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.trow:hover { background: var(--raised); }

/* What this fleet runs: the block, the computer, what it is doing, and the
   address it answers on. */
.block-row {
  display: grid;
  grid-template-columns: minmax(200px, 2fr) minmax(100px, 1fr) minmax(140px, 1.4fr) 104px 20px 16px;
  gap: 16px;
  align-items: center;
  text-align: left;
}
.block-row > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.block-row.thead { padding: 0 16px 4px; }
/* A row of a block written here goes nowhere, so it is not a button. */
.srow { padding: 12px 16px; border-radius: 2px; background: var(--surface); }
.block-cell { display: flex; align-items: center; gap: 12px; min-width: 0; }
.block-name { display: flex; flex-direction: column; min-width: 0; }
.block-name > * { overflow: hidden; text-overflow: ellipsis; }
/* Q10: a block a grant opens, with nothing to change about it. */
.grant-row { display: flex; align-items: center; gap: 12px; padding: 8px 16px; border-radius: 2px; background: var(--surface); }
.grant-row .block-name { flex: 1; }
/* The instance name inside a clickable row: a button, so a keyboard reaches
   the block page the row opens, and it reads as the text it replaces. */
.row-title {
  border: 0;
  padding: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.row-chev { width: 16px; height: 16px; color: var(--mu); }
/* The one way out of a row to the thing itself. It is the row's only second
   target, so it stays hidden until the row is under the pointer. */
.row-open { visibility: hidden; width: 20px; height: 20px; padding: 0; }
.trow:hover .row-open, .row-open:focus-visible { visibility: visible; }
/* The same rows on one computer's page: that page is already about the
   computer, so the column naming it is gone. */
.block-row-here { grid-template-columns: minmax(200px, 2fr) minmax(160px, 1.5fr) 104px 20px 16px; }
/* One block's page names one computer per role: the name, then its pill. */
.block-row-one { grid-template-columns: minmax(200px, 1fr) 104px; }
.blocks-here { align-self: stretch; gap: 8px; }
/* A row sits on a surface card here, so it takes the level above it. */
.blocks-here .block-row { align-self: stretch; }
.blocks-here .trow, .blocks-here .srow { background: var(--raised); }
.blocks-here .trow:hover { background: var(--raised-hi); }
.drafts { display: flex; flex-direction: column; gap: 8px; }
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: 8px; }
.card { display: flex; flex-direction: column; gap: 8px; padding: 16px; border-radius: 2px; background: var(--surface); cursor: pointer; }
.card:hover { background: var(--raised); }
.card-static { cursor: default; }
.card-static:hover { background: var(--surface); }
.card-title { margin: 0; padding: 0; border: 0; background: none; text-align: left; cursor: pointer; font: inherit; font-weight: 600; color: inherit; }
/* A card names itself the way a Blocks row does: the tile, then the name
   over what it is. */
.card-head { display: flex; align-items: center; gap: 12px; }
.card-tile { width: 40px; height: 40px; }
.card-id { display: flex; flex: 1; flex-direction: column; min-width: 0; }
.card-id > * { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* The name is the card: it takes two lines before it is cut, and a word
   with no break in it is broken rather than pushing the row wider. */
.card-head .card-title {
  display: -webkit-box;
  min-width: 0;
  overflow: hidden;
  overflow-wrap: anywhere;
  line-height: 1.3;
  white-space: normal;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.card-meta { margin: 0; color: var(--mu); }
/* A card gives its description one line; the whole of it is in the
   tooltip. */
.card > .note { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-list { display: flex; flex-direction: column; gap: 2px; align-self: stretch; }
.file-line { display: flex; gap: 12px; margin: 0; font-family: var(--mono); }
.tech { display: flex; flex-direction: column; gap: 8px; align-self: stretch; align-items: flex-start; }
.block-doc { align-self: stretch; width: 100%; }
.block-doc .md { align-self: stretch; }
/* A block's own page names it bigger than a card does. */
.block-tile { width: 40px; height: 40px; }
/* A connector's category mark, sized for the tile a letter would fill. */
.card-tile v-icon, .block-tile v-icon { width: 20px; height: 20px; }
/* A block's page is sections on the page, not cards: the page is already
   about one block. */
.sect { display: flex; flex-direction: column; gap: 8px; align-self: stretch; }
.sect-head { display: flex; align-items: center; gap: 8px; }
.sect-head .md-h { flex: 1; }
/* The name of the thing a page is about, with what it is doing beside it. */
.head-name { display: flex; align-items: center; gap: 8px; }
/* One choice the agent made while wiring a block, and the way to change it. */
.decision { display: flex; align-items: center; gap: 12px; padding: 8px 12px; }
.decision v-icon { color: var(--mu); }
.decision-text { flex: 1; min-width: 0; }
/* A button inside a row of content, not in a row of its own. */
.btn-row { height: 32px; padding: 0 12px; font-weight: 400; }
.empty p { margin: 0 0 8px; }
/* A name set in running text that goes somewhere. */
.link { border: 0; padding: 0; background: none; color: inherit; cursor: pointer; text-decoration: underline; }
.link:hover { color: var(--ink); }
/* The publisher under a name is a field, not running text: it goes to that
   person, and says so under the pointer. */
.card-id .link { text-decoration: none; }
.card-id .link:hover { text-decoration: underline; }

/* --- the block store ---------------------------------------------------- */

/* The one line or the one action of a store with nothing to show spans the
   grid the cards would fill. */
.cards > .empty { grid-column: 1 / -1; }
.store-more { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.store-more:empty { display: none; }
.store-more p { margin: 0; }
.store-text { margin: 0; text-wrap: pretty; }
/* The store's own warning about a block, as wide as the page: the line, its
   info mark, nothing else on it. */
.block-trust {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 12px 16px;
  border-radius: 2px;
  background: var(--raised);
}
/* One version of a block: its number, the day it was published, the copies
   here that are it, whether it is withdrawn, and Withdraw. The one on
   screen carries the accent's edge. */
.ver-row {
  display: grid;
  grid-template-columns: 96px 112px minmax(0, 1fr) auto auto;
  gap: 16px;
  align-items: center;
  text-align: left;
}
.ver-row > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ver-shown { box-shadow: inset 2px 0 0 var(--accent); }
/* A sentence in a row of facts wraps rather than being cut. */
.setting-value.setting-text { white-space: normal; }

.fold { display: flex; flex-direction: column; gap: 8px; align-self: stretch; align-items: flex-start; }
.fold-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--mu);
  cursor: pointer;
}
.fold-toggle:hover { color: var(--ink); }

.mono-block {
  align-self: stretch;
  max-height: 320px;
  margin: 0;
  padding: 12px;
  overflow: auto;
  border-radius: 2px;
  background: var(--page);
  color: var(--mu);
  font-family: var(--mono);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* --- the code off the computer's screen -------------------------------- */

.code { display: flex; align-items: center; gap: 8px; }
.code-box {
  width: 56px;
  height: 68px;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--ink);
  caret-color: var(--accent);
  font: 400 22px/1 var(--mono);
  text-align: center;
}
.code-box:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
.code-error .code-box { box-shadow: inset 0 0 0 2px var(--danger); }
.code-sep { color: var(--mu); font: 400 22px/1 var(--mono); }

/* --- fields ------------------------------------------------------------ */

.field {
  height: 40px;
  padding: 0 12px;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--ink);
}
.field:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
.field.invalid { box-shadow: inset 0 0 0 2px var(--danger); }
.field-mono { font-family: var(--mono); }
.name-field { width: 256px; text-align: center; }

/* --- changing a block, adding a connection ----------------------------- */

/* One panel holding every question, in the order they are answered. */
.form {
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-self: stretch;
  padding: 24px;
  border-radius: 2px;
  background: var(--surface);
}
.form-group { display: flex; flex-direction: column; gap: 4px; }
.form-label { color: var(--mu); }
/* An answer with nothing to choose: text to read, on the plane under the
   panel. */
.form-read { margin: 0; padding: 12px; border-radius: 2px; background: var(--page); }
.form-area { height: auto; min-height: 80px; padding: 10px 12px; resize: vertical; }
.form-note { margin: 0; }
.form-note:empty { display: none; }
.form-trust { display: flex; align-items: flex-start; gap: 12px; margin: 0; }
.form-trust v-icon { flex: none; margin-top: 2px; }

/* The add view is the page itself, not a panel: what must be answered,
   then the one strong button. */
.go-cancel { padding: 0; border: 0; background: none; color: var(--mu); cursor: pointer; }
.go-cancel:hover { color: var(--ink); }
.asks { display: flex; flex-direction: column; gap: 16px; align-self: stretch; }
.go { display: flex; flex-direction: column; gap: 12px; align-self: stretch; padding-top: 8px; }
/* The full-width button of a narrow column (B6). */
.btn-go { width: 100%; height: 44px; font-size: 15px; }
.go-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.go-trust { margin: 0; font-size: 13px; color: var(--mu); text-wrap: pretty; }
/* What a field resolves to, as part of it: the address a copy's name gives
   it, which is read and never typed (H2). */
.form-derived { margin: 0; font-size: 13px; color: var(--mu); }
.form-derived:empty { display: none; }
.form-more { display: flex; flex-direction: column; gap: 16px; align-self: stretch; }
/* No computer can run the block: why, and the one way on. */
.no-fit { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 12px 16px; border-radius: 2px; background: var(--surface); }
.no-fit > span { flex: 1; min-width: 240px; }
.no-fit .btn-secondary { background: var(--raised-hi); }
/* A panel form ends with Save across it, Cancel under it, and the one line
   a refusal takes under both (P8). */
.form-go { display: flex; flex-direction: column; gap: 12px; padding-top: 8px; }
.form-cancel { align-self: center; border: 0; padding: 0; background: none; color: var(--mu); cursor: pointer; }
.form-cancel:hover { color: var(--ink); }
/* How much of its limit a field holds, under its end; over it, the count
   is the refusal. */
.field-count-n { align-self: flex-end; font-size: 13px; color: var(--mu); }
.field-count-n.danger-ink { color: var(--danger); }
/* Changing a block that is already here: the panel takes an edge once the
   change has an outcome. */
.form { box-shadow: inset 2px 0 0 transparent; }
.form-ok { box-shadow: inset 2px 0 0 var(--ok); }
.form-bad { box-shadow: inset 2px 0 0 var(--danger); }
/* Apply before anything changed: the accent, held back, not a grey button. */
.form .btn-primary[disabled] { background: var(--accent); color: var(--accent-ink); opacity: 0.4; }

/* --- add a computer ---------------------------------------------------- */

.add { display: flex; flex: 1; flex-direction: column; align-items: center; justify-content: center; gap: 32px; }
.add-head { display: flex; align-items: center; gap: 8px; }
/* A line that annotates a field belongs to it, and takes no space when empty. */
.add-group { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.add-line { margin: 0; }
.add-line:empty { display: none; }
/* The first step (E7): where the computer runs, as two large cards. Each is
   a picture, a title and plain facts; neither carries a paragraph. */
.choices { display: grid; grid-template-columns: repeat(2, minmax(0, 320px)); gap: 16px; }
.choice { align-items: stretch; gap: 12px; padding: 20px; border: 0; text-align: left; font: inherit; color: inherit; }
.choice-pic { display: flex; justify-content: center; padding: 8px 0; }
.choice-title { font-size: 16px; font-weight: 600; }
.choice-body { display: flex; flex-direction: column; gap: 12px; }
.needs { margin: 0; padding-left: 18px; }
.needs li::marker { color: var(--mu); }
/* E8: a hosted card that can't be chosen drops below the page, its picture
   goes quiet, and the reason stands where the spec was. */
.choice-off, .choice-off:hover { background: var(--sunk); cursor: not-allowed; }
.choice-off .choice-pic { opacity: 0.4; filter: grayscale(1); }
.choice-off .choice-title { color: var(--mu); }
@media (max-width: 720px) { .choices { grid-template-columns: minmax(0, 320px); } }

/* E9: a hosted computer is named and nothing else, so the form is one
   column: its spec beside its picture, the name, then the way on. */
.add-form { display: flex; flex-direction: column; gap: 24px; width: 100%; max-width: 400px; }
.hosted-spec { display: flex; align-items: flex-start; gap: 16px; }
.hosted-spec .specs { flex: 1; min-width: 0; }
.add-go { display: flex; flex-direction: column; gap: 12px; }
.add-cancel { align-self: center; color: var(--mu); text-decoration: none; }

/* --- one computer's screen, in its own tab ------------------------------ */

/* The pane takes every pixel the bar leaves: xterm.js and noVNC size the
   terminal and the remote session from it. */
body.viewer { display: flex; flex-direction: column; }
.viewer-bar { display: flex; flex: none; align-items: center; gap: 16px; height: 56px; padding: 0 16px; background: var(--surface); }
.viewer-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.viewer-clip { display: flex; flex: none; padding: 8px 16px; background: var(--surface); }
.viewer-clip textarea { flex: 1; height: auto; padding: 8px 12px; font-family: var(--mono); resize: vertical; }
.viewer-main { position: relative; flex: 1; min-height: 0; }
.viewer-pane { position: absolute; inset: 0; overflow: hidden; background: var(--page); }
.viewer-console .xterm { height: 100%; padding: 8px; }
.viewer-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; max-width: 480px; margin: 48px auto; padding: 0 24px; }
/* noVNC's screen, and the note over it while it starts or after it closed. */
.viewer-rfb { position: absolute; inset: 0; }
.viewer-note { position: absolute; inset: 0; background: var(--page); }

/* --- the destructive modal --------------------------------------------- */

.modal {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: min(440px, calc(100vw - 48px));
  padding: 24px;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--ink);
  box-shadow: 0 24px 64px var(--shadow);
}
.modal::backdrop { background: var(--backdrop); }
.modal-head { display: flex; align-items: center; gap: 12px; }
.modal-title { margin: 0; font-weight: 600; }
.modal-line { margin: 0; }

.modal-err { margin: 0; color: var(--danger); }
.modal-err:empty { display: none; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }

.modal .btn-secondary { background: var(--raised-hi); }
.modal .btn[disabled] { background: var(--raised-hi); color: var(--mu); }

/* --- agent column ------------------------------------------------------ */

.agent { position: relative; display: flex; flex-direction: column; min-height: 0; background: var(--surface); }
/* The edge sits inside the column so it never covers the main column's
   scrollbar. Its line is the focus ring's, shown only while it is in use. */
.agent-edge { position: absolute; top: 0; bottom: 0; left: 0; z-index: 1; width: 6px; cursor: col-resize; touch-action: none; }
.agent-edge:focus-visible { outline: none; }
.agent-edge:is(:hover, :focus-visible, .dragging)::after { content: ""; position: absolute; inset: 0 auto 0 0; width: 2px; background: var(--accent); }
/* While the edge is held the pointer crosses text it must not select. */
body.resizing { cursor: col-resize; user-select: none; }
/* Q10: a house reached only through a grant has no infrastructure for the
   agent to act on, so the column is not there. */
body.shared { grid-template-columns: 220px minmax(0, 1fr); }
body.shared .agent { display: none; }
.agent-head { display: flex; flex: none; align-items: center; gap: 8px; height: 64px; padding: 0 16px; }
.agent-mark { width: 18px; height: 18px; color: var(--mu); }
.agent-title { font-weight: 600; }
.agent-state { margin-left: auto; }

.log {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  padding: 16px;
  overflow-y: auto;
  scrollbar-width: thin;
}
.log p { margin: 0; }

.msg { white-space: pre-wrap; overflow-wrap: break-word; }
.msg.user { width: fit-content; max-width: 85%; margin-left: auto; padding: 8px 12px; border-radius: 2px; background: var(--raised); }

.md > * { margin: 0; }
.md > * + * { margin-top: 8px; }
.md strong, .md-h { font-weight: 600; }
.md ul, .md ol { padding-left: 20px; }
.md li::marker { color: var(--mu); }
.md li + li { margin-top: 2px; }
.md code, .msg code { font-family: var(--mono); }
.md pre {
  padding: 12px;
  overflow-x: auto;
  border-radius: 2px;
  background: var(--page);
  font-family: var(--mono);
  white-space: pre;
}
/* A table sits on the plane a fence does and scrolls inside its own box. */
.md-table {
  width: fit-content;
  max-width: 100%;
  overflow-x: auto;
  border-radius: 2px;
  background: var(--page);
}
.md table { border-collapse: collapse; }
.md th, .md td { padding: 6px 12px; text-align: left; vertical-align: top; }
.md th { color: var(--mu); font-weight: 600; }
.md .md-center { text-align: center; }
.md .md-right { text-align: right; }
.md-url { color: var(--mu); }

/* The cursor trails the last block's text, not a line of its own below it. */
.streaming:empty::after,
.streaming > :last-child:not(ul, ol)::after,
.streaming > :is(ul, ol):last-child > li:last-child::after {
  content: "";
  display: inline-block;
  width: 0.5em;
  height: 1.1em;
  margin-left: 1px;
  vertical-align: text-bottom;
  background: var(--mu);
  animation: blink 1s steps(2) infinite;
}

.thinking-body {
  padding: 12px;
  border-radius: 2px;
  background: var(--page);
  color: var(--mu);
  white-space: pre-wrap;
}
/* One fold holds every pass of a turn's thinking, so a pass needs the gap
   .log takes away from a paragraph. */
.thinking-body.md > * + * { margin-top: 12px; }

.tool { display: flex; flex-direction: column; gap: 8px; }
.tool-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}
.tool.running .tool-head { cursor: default; }
.tool-mark { display: inline-flex; flex: none; }
.tool-verb { flex: 1; min-width: 0; }
.tool-chev { color: var(--mu); }
.tool-out { align-self: stretch; max-height: 260px; }
.tool.collapsed .tool-out { display: none; }

.chips { display: flex; flex: none; flex-wrap: wrap; gap: 4px; padding: 0 16px 8px; }
.chip {
  height: 28px;
  padding: 0 10px;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--mu);
  cursor: pointer;
}
.chip:hover { background: var(--raised-hi); color: var(--ink); }

.composer { display: flex; flex: none; align-items: flex-end; gap: 8px; padding: 0 16px 16px; }
.composer textarea {
  flex: 1;
  min-height: 40px;
  max-height: 160px;
  padding: 9px 12px;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--ink);
  resize: none;
}
.composer textarea:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }

.send { width: 40px; height: 40px; background: var(--accent); color: var(--accent-ink); }
.send:hover { background: var(--accent); color: var(--accent-ink); filter: brightness(1.1); }
.stop { width: 40px; height: 40px; background: var(--raised); color: var(--ink); }

/* --- pages without the shell ------------------------------------------- */

body.signed-out, body.notice { display: flex; align-items: center; justify-content: center; overflow: auto; }
.signin, .notice main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  width: 100%;
  max-width: 320px;
  padding: 32px;
}
.brand { margin: 0; font-weight: 600; }
.signin .brand { font-size: 22px; }
.notice main { gap: 8px; }
.notice h1 { margin: 0; font-size: 22px; font-weight: 600; }
.notice p { margin: 0; }
.notice .btn { margin-top: 16px; }
/* Choose your username: the field, its refusal 4px under it, the button. */
.ask { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; width: 100%; }
.ask-field { width: 100%; }

.setting { display: flex; align-items: center; gap: 16px; min-height: 48px; padding: 8px 16px; border-radius: 2px; background: var(--surface); }
.setting-label { flex: none; width: 160px; color: var(--mu); }
.setting .field { height: 32px; }
.setting-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One setting and, 4px under it, why it was refused. */
.settings-row { display: flex; flex-direction: column; gap: 4px; }
.icon-btn.save { background: var(--accent); color: var(--accent-ink); }
.icon-btn.save:hover { background: var(--accent); color: var(--accent-ink); filter: brightness(1.1); }
.seg { display: flex; gap: 2px; }
.seg button {
  height: 32px;
  padding: 0 12px;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--mu);
  cursor: pointer;
}
.seg button.on { background: var(--raised); color: var(--ink); }
.seg button.soon { cursor: not-allowed; }

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes blink { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* --- Access on a block (I6) --------------------------------------------- */

/* The address a block answers on, in the Blocks list: the mode under it
   says who can open it without opening the block. */
.web-cell { display: flex; flex-direction: column; min-width: 0; }
.web-cell .mono { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.access-note { margin: 0; }
.access-note:empty { display: none; }
.access-err:empty { display: none; }
/* The address row: the link takes the width, the copy icon sits at its end. */
.ac-address { display: flex; align-items: center; gap: 8px; padding: 10px 16px; border-radius: 2px; background: var(--surface); }
.ac-address a { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: inherit; }
/* Three cards side by side, the chosen one ringed in the accent: the
   choice and what it means are one thing, so the meaning is not a tooltip. */
.ac-modes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.ac-mode {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 16px;
  border: 0;
  border-radius: 2px;
  background: var(--surface);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.ac-mode:hover { background: var(--raised); }
.ac-mode.on { box-shadow: inset 0 0 0 2px var(--accent); }
.ac-mode[disabled] { cursor: not-allowed; opacity: 0.6; }
.ac-mode-name { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.ac-mode.on .ac-mode-name v-icon { color: var(--accent); }
.ac-mode-name v-icon { color: var(--mu); }
/* I6: one row per house, team or person that can open it — the mark, the
   name, and the way to take it away. */
.ac-person { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-radius: 2px; background: var(--surface); }
.ac-person .pf-face-sm { width: 28px; height: 28px; font-size: 13px; }
.ac-person .pic { flex: none; }
/* A team has no picture of its own, so it takes the mark its chip carries,
   on a tile the width of a house's picture. */
.ac-tile { width: 36px; height: 28px; }
.ac-tile v-icon { width: 16px; height: 16px; }
.ac-person-id { display: flex; flex: 1; min-width: 0; align-items: center; gap: 8px; }
.ac-person-id > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-add { display: flex; gap: 8px; }
.ac-add .field { flex: 1; min-width: 0; }
/* Access that came through whoever runs it says where it came from, where
   a row of this list's own offers Remove. */
.ac-faces { display: flex; flex: none; }
.ac-faces > * { margin-left: -6px; box-shadow: 0 0 0 2px var(--surface); }
.ac-faces > :first-child { margin-left: 0; }
.ac-via { flex: none; color: var(--mu); white-space: nowrap; }
.btn-sm { height: 28px; padding: 0 10px; }

/* --- Residents, Teams and a team (Y2, Y3, Y5, Y6) ---------------------- */

.pf-face-sm { flex: none; width: 32px; height: 32px; border-radius: 2px; background: var(--person); object-fit: cover; }
.pe-who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.pe-name { display: flex; flex-direction: column; min-width: 0; }
.pe-name > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pe-name-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.pe-name-row > :first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A team standing beside other things is a chip, and the chip is the way to
   its page; a page about the team writes "Team <name>" as text instead. */
.team-chip {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  height: 24px;
  padding: 0 8px;
  border: 0;
  border-radius: 2px;
  background: var(--raised);
  color: var(--ink);
  cursor: pointer;
}
.team-chip:hover { background: var(--raised-hi); }
.team-chip v-icon { width: 14px; height: 14px; }
/* A modal stands one plane above the page, so the rows it holds rise too. */
.modal .ho-row { background: var(--raised-hi); }

/* --- houses and teams (Q1–Q8) ------------------------------------------- */

/* A house reads as a place, so its tile and grid are the computer view's. */
.ctile-on { box-shadow: inset 0 0 0 2px var(--accent); }
.ctile-dash .ctile-name { color: var(--mu); }
.hs-faces { display: flex; }
.hs-faces > * { margin-left: -6px; box-shadow: 0 0 0 2px var(--surface); }
.hs-faces > :first-child { margin-left: 0; }
.ctile-on .hs-faces > * { box-shadow: 0 0 0 2px var(--raised); }
.hs-face { display: flex; flex: none; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 2px; background: var(--person); font-size: 10px; font-weight: 600; object-fit: cover; }
/* The switcher carries the house itself, not an initial, and the whole row
   is the one button to the houses. */
.switch-pic, .switch-pic .pic { display: block; flex: none; }
.switch-open {
  display: flex;
  flex: 1;
  align-items: center;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
/* Q9: the pen answers the pointer and the keyboard, and is not there the
   rest of the time, so a grid of houses stays pictures and names. */
.hs-tile { position: relative; display: flex; }
.hs-edit {
  display: flex;
  visibility: hidden;
  position: absolute;
  top: 6px;
  right: 6px;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--mu);
  cursor: pointer;
}
.hs-tile:hover .hs-edit, .hs-edit:focus-visible { visibility: visible; }
.hs-edit:hover { color: var(--accent); }
/* Q11: the house's picture beside the one button that changes it. */
.hs-picrow { display: flex; align-items: center; gap: 16px; }
/* Q13: a house with more than one infrastructure splits into sections, and
   the heading says which one and, when a team runs it, which team and
   whose. The pen is smaller than a page's icon buttons: it sits in a
   heading. */
.sect-infra { display: flex; align-items: center; gap: 8px; color: var(--mu); }
.sect-infra .icon-btn { width: 28px; height: 28px; }
/* Y2: the house's own picture and its name. */
.hp-head { gap: 16px; }
.hp-head .pic, .hp-head .pic-btn { flex: none; }
/* Y5: a team of the person's, what it runs, and what they are in it. */
.teams-row { display: grid; grid-template-columns: minmax(160px, 1.4fr) minmax(140px, 1fr) 88px 16px; gap: 16px; align-items: center; text-align: left; }
.teams-row > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.teams-title { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.teams-runs { display: flex; align-items: center; gap: 12px; min-width: 0; color: var(--mu); }
.teams-run { display: flex; align-items: center; gap: 8px; min-width: 0; }
.teams-run > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One row of a team's page: a member, an infrastructure it runs, a notice
   nobody has dismissed yet. */
.tm-row { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-radius: 2px; background: var(--surface); text-align: left; }
.tm-name { display: flex; flex: 1; flex-direction: column; min-width: 0; }
.tm-name > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-title { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.tm-grow { flex: 1; min-width: 0; overflow: hidden; text-align: left; text-overflow: ellipsis; white-space: nowrap; }
/* Handing an infrastructure over: one team per row, picked like a radio. */
.ho-row { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border: 0; border-radius: 2px; background: var(--surface); color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.ho-row:hover { background: var(--raised); }
.ho-radio { flex: none; width: 18px; height: 18px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--dim); }
.ho-radio.on { box-shadow: inset 0 0 0 5px var(--accent); }
/* A team where it is named as the one running something (Q13): the users
   mark and the name, inside the row that picks it. */
.chip-team { display: inline-flex; align-items: center; gap: 6px; cursor: inherit; }
.ho-row:hover .chip-team { background: var(--raised-hi); color: var(--ink); }
/* The house's own people: who they are, and whether they own the house. */
.mb-row { display: grid; grid-template-columns: minmax(200px, 2fr) 120px 32px; gap: 16px; align-items: center; }
.mb-row.thead { padding: 0 16px 4px; }

/* --- a profile in the block store ---------------------------------------- */

/* The profile's sections stand further apart than a list's rows do: each
   one is about something else (P1). */
.profile { gap: 32px; }
.pf-head { display: flex; align-items: flex-start; gap: 20px; }
/* A store profile has no picture, so its face is a letter on the tile a
   person's face takes. */
.pf-face {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 80px;
  height: 80px;
  border-radius: 2px;
  background: var(--person);
  font-size: 32px;
  font-weight: 600;
}
.pf-id { display: flex; flex: 1; flex-direction: column; gap: 4px; min-width: 0; }
/* The description and the link each start a new thought, so they take the
   step between components rather than the one inside one. */
.pf-bio { margin-top: 4px; text-wrap: pretty; }
.pf-meta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; color: var(--mu); }
/* A link is a machine value and the one thing in that line to follow, so it
   keeps full ink. */
.pf-link { font-family: var(--mono); color: var(--ink); text-decoration: underline; }
.pf-acts { display: flex; flex: none; gap: 8px; }
/* The button keeps its size when a refusal hangs under it. */
.pf-acts-col { flex: none; align-items: flex-end; }
.pf-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.pf-stat { display: flex; flex-direction: column; gap: 2px; padding: 16px; border-radius: 2px; background: var(--surface); }
.pf-stat-n { font-weight: 600; }
/* P3: nothing published yet. The mark, and on your own profile the one way
   to change that. */
.pf-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  min-height: 280px;
  padding: 32px 16px;
}
.pf-empty v-icon { width: 96px; height: 96px; color: var(--dim); }
/* A follower or a followed account: the way to their profile. */
.pf-person { display: flex; align-items: center; gap: 12px; font: inherit; text-align: left; }
