/* muster — one stylesheet, no framework, no build step.
 *
 * The design is dark by construction rather than by preference: this is a
 * dashboard somebody leaves open beside their work, and it should not be the
 * brightest thing on the desk. There is no light band, so every colour below is
 * stated once and the page paints its own ground.
 *
 * Two rules carry most of the meaning and are worth reading before the code:
 *
 *   Azure means it is moving. Amber means it is waiting for you.
 *   Never azure for a warning, never amber for something healthy.
 *
 *   There are no caps. Nothing shows a denominator, a quota or a ceiling —
 *   capacity is what is happening ("88 sessions running") and, between
 *   companies, a share of the fleet, which is a distribution and not a limit.
 *   When every machine is busy the scheduler makes work wait its turn; it
 *   never refuses it. So amber on a chart means work started late, never
 *   "over the limit".
 */

/* Served from the binary. An interface that has to reach a font CDN to look
   right is one that looks wrong on the machines this runs on. */
@font-face {
  font-family: "Geist"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/static/geist-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/static/geist-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist Mono"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/static/geist-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/static/geist-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Instrument Serif"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/instrument-serif-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Instrument Serif"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/instrument-serif-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  color-scheme: dark;

  --ground: #0b1020;
  --bar: #0d1324;
  --panel: #0f1526;
  --panel-2: #131b2e;
  --panel-3: #1a2338;
  --terminal: #070b16;

  --line: rgba(255, 255, 255, .08);
  --line-soft: rgba(255, 255, 255, .06);
  --line-strong: rgba(255, 255, 255, .12);
  --hover: #182034;
  --track: #1e2942;

  --accent: #6ba6ff;
  --accent-hover: #94c2ff;
  --accent-ink: #04122b;
  --accent-mid: #4f8ae0;
  --accent-deep: #2f5aa0;
  --accent-deepest: #22406e;

  --warn: #e8a33d;
  --warn-tint-bg: #161a1a;
  --warn-tint-border: rgba(232, 163, 61, .32);
  --warn-text: #c9bda6;
  --warn-title: #f2d9b0;
  --fail: #e2603f;

  --text: #e8ecf4;
  --text-bright: #f2f5fa;
  --text-2: #c3cbdb;
  --text-3: #a8b2c6;
  --muted: #8a95ad;
  /* Both measured. Small text needs 4.5 against these grounds, so muted-2 is
     lifted eight on each channel to clear it everywhere. muted-3 cannot be
     fixed the same way — the lift that clears the threshold lands on muted-2 —
     so it keeps its value and stops carrying words: it is for the inert
     progress bar and the quiet dot, which are marks and not text. */
  --muted-2: #77839e;
  --muted-3: #5b6784;
  --inert: #3a4560;

  --sans: "Geist", system-ui, -apple-system, sans-serif;
  --mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --serif: "Instrument Serif", Georgia, serif;

  --r-panel: 10px;
  --r-nested: 8px;
  --r-control: 7px;
  --r-small: 6px;
  --r-tag: 5px;
}

/* The only animations in the product. Absence of animation is information: a
   queued, failed or unknown topic must be visibly still. */
@keyframes musterPulse { 0%,100% { opacity:1; transform:scale(1) } 50% { opacity:.35; transform:scale(.72) } }
@keyframes musterSlide { 0% { background-position:100% 0 } 100% { background-position:-100% 0 } }
@keyframes musterFlow  { 0%,100% { opacity:.35; transform:translateX(0) } 50% { opacity:1; transform:translateX(3px) } }
@keyframes musterCaret { 0%,49% { opacity:1 } 50%,100% { opacity:0 } }
@keyframes musterDots  { 0% { opacity:.2 } 20% { opacity:1 } 100% { opacity:.2 } }
@keyframes musterSpin  { to { transform: rotate(360deg) } }

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

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

/* The hidden attribute means hidden. Its own rule is display: none on a bare
   element selector, which any class that sets a display beats — and several
   here do. */
[hidden] { display: none !important; }

body {
  margin: 0; background: var(--ground); color: var(--text);
  font-family: var(--sans); font-size: 14px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }
:focus { outline: none; }
:focus-visible { outline: 1.5px solid var(--accent); outline-offset: 2px; }
::selection { background: rgba(107, 166, 255, .25); }

/* Instrument Serif is for display numerals and titles only — never body, never
   a label. */
h1, h2, h3, .serif { font-family: var(--serif); font-weight: 400; }
h1 { font-size: 44px; line-height: 1.05; margin: 0; color: var(--text-bright); letter-spacing: -.005em; }
h2 { font-family: var(--sans); font-size: 15px; font-weight: 600; margin: 0; }
h3 { font-size: 26px; line-height: 1.15; margin: 0; color: var(--text-bright); }

/* A subject that is an identifier rather than a title: a path, a command, a
   branch, the name of a tmux session. It takes the h1's rank — it is the thing
   the page is about — and keeps the mono face, because a reader compares it
   character by character against something on a machine. Not 44px: the same
   string set in mono is close to twice as wide as in the serif, and 30px still
   outranks the 26px a section is set at. */
h1.mono {
  font-family: var(--mono); font-size: 30px; font-weight: 400;
  letter-spacing: -.01em; overflow-wrap: anywhere;
}

/* The heading holds itself off the table underneath it. Scoped to a section's
   direct children: an h3 inside a panel header is a flex item beside its
   badges, and margin there would push them apart. */
section > h3, section > h4 { margin: 0 0 14px; }

/* A band of sections, and the seam between two of them. A page whose sections
 * are all spaced alike reads as a list of peers, whatever order they are in:
 * the machine page runs its own state, then who it is for, then what is
 * happening on it. One hairline at each seam, and the space that carries it —
 * never a box, which would make three pages out of one. */
section.band { margin-top: 46px; padding-top: 32px; border-top: 1px solid var(--line-soft); }
section > h4 { font-family: var(--sans); font-size: 13px; font-weight: 600; margin-top: 26px; }

.mono { font-family: var(--mono); }
.num { font-variant-numeric: tabular-nums; }
.muted { color: var(--muted); }
/* A page's one-line subject when that line is not good news. Amber, because it
   is the machine waiting for somebody — never red: nothing here is broken, and
   most of these states are a step nobody has taken yet. */
.sub.warned { color: var(--warn-title); }
.dim { color: var(--muted-2); }

/* A hairline between two groups of a menu or a list. Another of the design
   kit's classes that arrived in the templates without its rule: four of them
   are written — three in the reader's own menu, one in the command panel —
   and every one of them has been drawing nothing at all. */
.sep { height: 1px; background: var(--line); }

/* The uppercase micro-label that names a group without competing with it.
   Two spellings for one thing, and that is a repair rather than a choice:
   `.kicker` is this stylesheet's own word, `.lab` is the design's, and eight
   templates were committed using the second while no rule in here matched it
   — so every one of those labels drew at the body's size in the body's
   colour, and a name and its number read as two facts. Both names, one rule,
   until the templates say one word. */
.kicker, .lab {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); margin: 0;
}
.sub { font-size: 14.5px; color: var(--text-3); margin: 6px 0 0; line-height: 1.55; }

/* The name, which is the control that renames it. It has to look like the
   heading it is until somebody goes near it, so the button borrows every
   inherited value and adds only the pencil — dim by default and lit on hover,
   present at rest because a hover-only affordance does not exist on a phone. */
.rename {
  display: inline-flex; align-items: center; gap: 8px;
  border: none; background: none; padding: 0; cursor: pointer;
  font: inherit; color: inherit; letter-spacing: inherit;
}
.rename svg { width: 13px; height: 13px; color: var(--muted-2); flex: none; }
.rename:hover { background: none; color: var(--text-bright); }
.rename:hover svg { color: var(--accent); }

/* A state pill that is also the switch for that state. The connection pill
   beside it reports what the machine is doing and must not invite a click, so
   it stays a span and this styling never reaches it. The pointer, the hover and
   the focus ring are the whole difference between the two. */
.cond.switch { cursor: pointer; font-family: var(--mono); position: relative; }
.cond.switch:hover { border-color: currentColor; background: rgba(255, 255, 255, .04); }

/* The hit area, which does not shrink with the pill. A control that is 18px
   tall is a control a thumb misses, so the switch keeps a target of its own,
   stretched past the pill and invisible — the pill is the thing you see, not
   the thing you have to hit. */
.cond.switch::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 34px; transform: translateY(-50%);
}

/* An aside beside something else — the "last seen" that hangs off a
   disconnected machine's pill, in the page's own header and in a row of the
   machines list. It is smaller than the name it hangs under, and the class
   means the same thing wherever it is written. */
.aside { font-size: 11px; }

main { padding: 32px; display: flex; flex-direction: column; gap: 24px; }
.head { display: flex; align-items: flex-end; gap: 20px; flex-wrap: wrap; }
.head .grow { flex: 1; min-width: 0; }
/* Measurements at the right end of a page's head: the label then the value, on
   one line, in the order somebody says them. Never a sentence, and never a
   column of stacked pairs — three of those beside a title is a dashboard where
   the title should be. */
.figures { display: flex; align-items: baseline; gap: 26px; flex: none; flex-wrap: wrap; }
.figure { display: inline-flex; align-items: baseline; gap: 8px; }
.figure .num { font-family: var(--mono); font-size: 15px; line-height: 1; color: var(--text-bright); }
/* The denominator of a figure that is a share of something — 12/18 at work.
   Dimmer, because the number that changes is the first one. */
.figure .num .of { color: var(--muted-3); }

/* Day one's checklist: the whole way to a first mission. A checked step keeps
   what it holds, the next one is the only primary button, and one the reader
   cannot take says who it waits on in amber rather than vanishing. */
.setup { margin: 0; padding: 0; list-style: none; max-width: 880px;
  border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--panel); overflow: hidden; }
.setup-row { display: flex; align-items: center; gap: 16px; min-height: 68px; box-sizing: border-box;
  padding: 12px 20px; border-top: 1px solid var(--line-soft); }
.setup-row:first-child { border-top: 0; }
.setup-now { background: var(--panel-2); }
.setup-mark { display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 28px; height: 28px; box-sizing: border-box; border-radius: 50%;
  border: 1.5px solid var(--muted-3); font-family: var(--mono); font-size: 12px; color: var(--muted-2); }
.setup-done .setup-mark { border-color: var(--accent-deepest); background: var(--accent-deepest); color: var(--accent-hover); }
.setup-now .setup-mark { border-color: var(--accent); color: var(--accent); }
.setup-waiting .setup-mark { border-color: var(--warn); color: var(--warn); }
.setup-what { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.setup-title { font-size: 15px; font-weight: 500; color: var(--text-bright); }
.setup-done .setup-title { color: var(--text-3); }
.setup-later .setup-title { color: var(--muted); }
.setup-detail { font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
.setup-state { font-size: 11px; color: var(--muted-2); white-space: nowrap; }
.setup-waiting .setup-state { padding: 5px 9px; border: 1px solid var(--warn-tint-border); border-radius: var(--r-tag); color: var(--warn); }
@media (max-width: 640px) {
  .setup-row { flex-wrap: wrap; padding: 12px 14px; gap: 10px 12px; }
  .setup-what { flex-basis: calc(100% - 40px); }
  .setup-row .btn, .setup-state { margin-left: 40px; }
}
.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.spacer { flex: 1; }
.scroll { overflow-x: auto; }

/* ---------------------------------------------------------------------------
   The rail

   Down the side of a desk, across the top of a phone. One element in two
   shapes rather than two elements: a strip that holds the company you are in
   and the person you are cannot be two strips that have to agree.

   Fixed and not a flex column beside main, because main is not the only thing
   in the body — the toasts and the dock are siblings of it, and a
   body laid out as a row would make each of them a column of its own. Fixed
   plus a padding on the body is three rules and no reflow.
   --------------------------------------------------------------------------- */
body { padding-left: 216px; }
.rail {
  position: fixed; left: 0; top: 0; bottom: 0; width: 216px; z-index: 20;
  display: flex; flex-direction: column; gap: 2px;
  padding: 16px 12px; background: var(--bar);
  border-right: 1px solid var(--line);
}
.brand svg { display: block; flex: none; }
/* The mark takes the accent, wherever it is drawn. It used to inherit the
   brand row's bright text, and the brand row is gone: the head of the rail is
   the company's own name now. */
.switch > summary > svg { display: block; flex: none; color: var(--accent); }
/* The head of the rail on the installation's plane, in the place the company
   switcher holds everywhere else. The same box as that summary — same padding,
   same radius, same two lines of type — because it answers the same question
   at the same glance: which of these am I standing in. It is a link and not a
   disclosure: there is one installation. */
.plane-head {
  display: flex; align-items: center; gap: 9px; padding: 7px 8px;
  border-radius: var(--r-nested); color: var(--text-2); flex: none; min-width: 0;
}
.plane-head > svg { display: block; flex: none; color: var(--accent); }
.plane-head:hover { background: var(--hover); color: var(--text-bright); }
.plane-head.on { background: var(--panel-3); color: var(--text-bright); }
/* The mark, in the plane's own colour. Amber is what this product paints the
   installation with wherever it names it — the door at the foot of the
   reader's menu, the count of machines not answering — and the head of the
   rail is where a reader finds out which plane they are standing on. It is the
   one thing on the page that differs from the company's rail in colour rather
   than in words, and it is doing the same job the kicker under it does: this
   is not a customer's view. */
.rail-admin .plane-head > svg { color: var(--warn); }
/* Home, in the switcher's box. The arrow at the right end is where the
   switcher's chevron is, and it says the same kind of thing about the row: the
   chevron opens something, this leaves. */
.rail-home .switch-who { flex: 1; min-width: 0; }
.rail-home-out { width: 14px; height: 14px; flex: none; color: var(--muted-3); }
.rail-home:hover .rail-home-out { color: var(--text-3); }

.rail nav { display: flex; flex-direction: column; gap: 2px; margin-top: 10px; }
/* The settings subjects as a row, for a phone only. Above 46rem the rail has
   them and a second copy would be two controls for one question; under it the
   rail is the tab bar and the tab bar is the way out, so this is the only
   place the subjects can be. */
.rail-head {
  margin: 8px 0 4px; padding: 0 10px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted-3);
}
.rail nav a {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border-radius: var(--r-nested); font-size: 13.5px; font-weight: 500;
  color: var(--text-3); white-space: nowrap;
}
.rail nav a svg { width: 16px; height: 16px; flex: none; }
.rail nav a:hover { color: var(--text-bright); background: var(--hover); }
.rail nav a.on { color: var(--text-bright); background: var(--panel-3); font-weight: 600; }
/* How many things are waiting, in amber, wherever the count is drawn: the
   rail's Company row, the command panel's own Go group, and anywhere else that
   count belongs. It was scoped to the rail, so the panel had to restate it. */
.badge {
  padding: 0 5px; border-radius: var(--r-tag);
  background: var(--warn); color: var(--accent-ink); font-family: var(--mono);
  font-size: 10.5px; font-weight: 500;
}
.rail nav a .badge { margin-left: auto; }
.corner { margin-top: auto; display: flex; flex-direction: column; gap: 4px; padding-top: 10px; border-top: 1px solid var(--line); }

/* The bar across the top of the content column: the one way in, and what is
   waiting. 52px, a bottom rule, and nothing else in it — a bar that collects
   controls is a second navigation. */
.topline {
  display: flex; align-items: center; gap: 14px; height: 52px; flex: none;
  padding: 0 22px; border-bottom: 1px solid var(--line);
}
/* Named for what it is rather than for what it looks like: .compose was
   already taken by the message editor on a topic's page, and two classes with
   one name is the later one winning silently. */
.wayin {
  display: flex; align-items: center; gap: 9px; flex: 1; max-width: 560px;
  padding: 7px 12px; border: 1px solid var(--line-strong); border-radius: 9px;
  background: var(--bar); color: var(--muted-3); font-size: 13px;
}
.wayin:hover { border-color: rgba(107, 166, 255, .5); color: var(--text-2); }
.wayin svg { width: 14px; height: 14px; flex: none; color: var(--muted-3); }
/* The field, when the bar is the field. It is the same box as the link — same
   height, same border, same ground — so that the one control does not change
   shape between the page that owns it and the pages that point at it. */
.wayin-says {
  flex: 1; min-width: 0; padding: 0; border: 0; background: none;
  font-family: var(--sans); font-size: 13px; color: var(--text);
}
.wayin-says::placeholder { color: var(--muted-3); }
.wayin-says:focus { outline: none; }
.wayin:focus-within { border-color: var(--accent); }
.wayin-go {
  flex: none; margin: -3px -8px -3px 0; padding: 5px 11px; border-radius: var(--r-small);
  border: 0; background: var(--accent); color: var(--accent-ink);
  font-family: var(--sans); font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.wayin-go:hover { background: var(--accent-hover); }
.wayin-none { color: var(--muted-3); }

/* A keycap: the chord that reaches a control, drawn as the key it is. Bare,
   because it is written in the bar, in the reader's menu and in the command
   panel — three places, one look. */
.key {
  padding: 1px 5px; font-size: 10.5px; color: var(--muted-3);
  border: 1px solid var(--line-strong); border-radius: var(--r-tag);
}
.wayin .key { margin-left: auto; }
.topline-end { display: flex; align-items: center; gap: 14px; margin-left: auto; }

/* ---------------------------------------------------------------------------
   The company network: which companies read which, drawn as an org chart.

   One list of rows, in the order a tree is read, each set one column to the
   right of the company that reads it. On a desk that is an org chart with
   elbows drawn by netmap.js from where the rows land; on a phone the columns
   are 20px indents and the elbows still fit. An inspector beside it holds the
   acts. Every row is a link, so nothing here needs the script to be reached.
   ------------------------------------------------------------------------ */
.netmap { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
.netmap-canvas {
  position: relative; padding: 22px 24px 24px; min-height: 320px;
  background-color: var(--panel); border: 1px solid var(--line); border-radius: var(--r-panel);
  background-image: radial-gradient(rgba(255, 255, 255, .06) 1px, transparent 1px);
  background-size: 18px 18px; background-position: 9px 9px;
}
.netmap-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
/* Visible lines: the 16% white of the first drawing vanished on a laptop. */
.netmap-edges path { fill: none; stroke: rgba(255, 255, 255, .38); stroke-width: 1.5; transition: stroke .15s, opacity .15s; }
.netmap-edges path.also { stroke: var(--warn); stroke-dasharray: 4 4; opacity: .8; }
.netmap-edges path.lit { stroke: var(--accent); stroke-width: 2; }
.netmap-edges path.also.lit { stroke: var(--warn-title); }
.has-sel .netmap-edges path:not(.lit) { opacity: .3; }

.netmap-trail { margin: -4px 0 14px; font-size: 11px; color: var(--muted-2); position: relative; z-index: 1; }
.netmap-trail a { color: var(--muted); }
.netmap-trail a:hover { color: var(--text-bright); }
.netmap-trail span { color: var(--text-2); }

/* The depth is a grid: a fixed column per level, every row's left edge on it. */
.tree { --indent: 264px; position: relative; z-index: 1; display: flex; flex-direction: column; gap: 10px; }
.row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; margin-left: calc(var(--d) * var(--indent)); min-width: 0; }
.tree-divider { display: flex; align-items: center; gap: 10px; padding: 12px 0 2px; }
.tree-divider .mono { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted-2); white-space: nowrap; }
.tree-divider .rule { flex: 1; border-top: 1px dashed var(--line-strong); }
.netmap-none { margin: 0; padding: 10px 0; font-size: 13px; color: var(--muted-2); }

.node {
  display: flex; align-items: center; gap: 10px; padding: 0 12px; height: 56px; width: 248px; box-sizing: border-box;
  background: var(--panel-2); border: 1px solid var(--line-strong); border-radius: var(--r-nested);
  color: var(--text); text-decoration: none; text-align: left; flex: none;
  transition: border-color .15s, opacity .15s, box-shadow .15s;
}
.node:hover { border-color: var(--accent-mid); color: var(--text-bright); }
.node.lit { border-color: var(--accent-mid); }
.node.sel { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(107, 166, 255, .18); color: var(--text-bright); }
.has-sel .node:not(.lit):not(.sel) { opacity: .5; }
.node-body { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
.node-name { font-size: 13.5px; font-weight: 600; letter-spacing: -.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.node-meta { font-size: 10px; color: var(--muted-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.node-loose { color: var(--warn-text); }
.node-tag { color: var(--accent); }
.node-also { color: var(--warn-text); }
/* What needs the reader, at the right edge of every card, so a column of
   cards is scanned top-down for amber. */
.node-wait {
  flex: none; padding: 0 6px; border-radius: var(--r-tag); line-height: 18px;
  background: var(--warn); color: var(--accent-ink); font-family: var(--mono); font-size: 10.5px; font-weight: 500;
}
.node-hub {
  width: 30px; height: 30px; border-radius: 50%; flex: none; display: grid; place-items: center;
  color: var(--accent); background: rgba(107, 166, 255, .1); border: 1px solid rgba(107, 166, 255, .35);
  font-size: 18px; font-weight: 300; line-height: 1;
}
.node-hub svg { width: 17px; height: 17px; }
/* A company that reads others: its face, with the hub as a badge on the
   corner. The badge says why this row has rows under it; the face says it is
   the same business as anywhere else in the product. */
.node-face { position: relative; flex: none; width: 26px; height: 26px; }
.node-hub-sm {
  position: absolute; right: -6px; bottom: -6px; width: 15px; height: 15px;
  background: var(--panel-2); border-color: var(--accent-mid); color: var(--accent);
}
.node-hub-sm svg { width: 10px; height: 10px; }
.node-company.here .node-hub-sm, .node-group.here .node-hub { background: var(--accent); color: var(--accent-ink); }
.node-group { border-color: rgba(107, 166, 255, .28); }
.node-new { border-style: dashed; background: transparent; color: var(--muted); }
.node-new .node-hub { color: var(--muted); background: transparent; border-color: var(--line-strong); }
.node-new:hover { color: var(--text-bright); }
.node-new:hover .node-hub { color: var(--accent); border-color: var(--accent-mid); }
.node-company.loose { border-style: dashed; }
/* The chip at the third level: what is under this one is a page of its own. */
.node-more {
  flex: none; font-size: 10.5px; color: var(--accent); padding: 4px 8px; margin-left: 38px;
  border: 1px dashed rgba(107, 166, 255, .45); border-radius: var(--r-tag); white-space: nowrap;
}
.node-more:hover { color: var(--accent-hover); border-color: var(--accent); }

/* The inspector. Every panel is in the page; :target shows one without the
   script, the script's `on` shows one with it, and the default shows when
   neither does. `scripted` on the section is what lets the script take over
   from :target without the two disagreeing. */
.netmap-insp {
  position: sticky; top: 14px; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-panel); padding: 18px 20px 20px; min-height: 320px;
}
.insp { display: none; }
.insp.on { display: block; }
.netmap:not(.scripted) .insp:target { display: block; }
.netmap:not(.scripted) .netmap-insp:not(:has(.insp:target)) .insp-default { display: block; }
.insp-kicker { margin: 0 0 6px; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted-2); }
.insp .net-rows + .insp-kicker, .insp .hint + .insp-kicker, .insp .net-add + .insp-kicker, .insp .insp-legend { margin-top: 16px; }
.insp .net-rows + .net-add, .insp .hint + .net-add { margin-top: 12px; }
.insp-sum { margin: 0 0 16px; font-size: 12px; color: var(--text-2); }
.insp-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 4px; }
.insp-title { margin: 0; font-size: 20px; line-height: 1.15; min-width: 0; }
.insp-title .rename { font: inherit; color: inherit; }
.insp .aside { display: block; margin-bottom: 14px; }
.insp .row { margin-bottom: 4px; }
.insp .row + .insp-kicker { margin-top: 18px; }
.insp-legend { margin: 0; display: flex; flex-direction: column; gap: 8px; font-size: 12px; color: var(--muted); }
.insp-legend div { display: flex; align-items: center; gap: 10px; }
.insp-legend dt, .insp-legend dd { margin: 0; }
.legend-line { display: inline-block; width: 26px; height: 0; border-top: 2px solid rgba(255, 255, 255, .38); vertical-align: middle; }
.legend-line.also { border-top: 2px dashed var(--warn); }
.legend-wait {
  display: inline-block; padding: 0 6px; border-radius: var(--r-tag); line-height: 18px;
  background: var(--warn); color: var(--accent-ink); font-family: var(--mono); font-size: 10.5px;
}
.insp-end { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--line-soft); font-size: 12.5px; }
.insp-end .link { font-size: 12.5px; }

/* A sign-in waiting in its row on the reader's own page: the link, the code,
   what the CLI said, and the field for the code coming back. A block and not
   a dialog, because the page reloads itself and a dialog is closed by a
   reload. */
.flow { display: flex; flex-direction: column; gap: 8px; padding: 10px 0 2px; max-width: 520px; }
.flow p { margin: 0; font-size: 13.5px; }
.flow .row { gap: 8px; flex-wrap: nowrap; }
.flow .grow { min-width: 0; }
.flow > .btn { align-self: flex-start; }

.net-rows { display: flex; flex-direction: column; }
.net-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 0; border-bottom: 1px solid var(--line-soft);
}
.net-row:last-child { border-bottom: 0; }
.net-name { font-size: 13.5px; color: var(--text-bright); }
.net-name:hover { color: var(--accent-hover); }
.net-by { font-size: 10.5px; color: var(--muted-2); }
.net-add { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.net-add label { font-size: 12px; color: var(--muted); }
.net-add select { min-width: 0; flex: 1; }
p.net-add { margin-bottom: 0; }
.btn.sm { padding: 4px 9px; font-size: 12px; }
/* The panel's one act. `.on` is the same control lit rather than offered, for
   the group the reader is already standing in; it is a span and not a link
   because the page it would go to is the one they came from. */
.net-read { flex: none; }
.net-read.on { cursor: default; color: var(--muted); border-color: var(--line); }

@media (max-width: 64rem) {
  .netmap { grid-template-columns: 1fr; }
  .netmap-insp { position: static; min-height: 0; }
  .tree { --indent: 120px; }
}
@media (max-width: 46rem) {
  /* The columns become indents and the card takes the width left: the same
     rows, the same elbows, drawn from where these land. */
  .netmap-canvas { padding: 16px 14px 18px; }
  .tree { --indent: 20px; }
  .node { width: auto; flex: 1; min-height: 44px; height: auto; padding: 8px 10px; gap: 8px; }
  .node-name { white-space: normal; }
  .node-meta { white-space: normal; }
  .node .av, .node-hub { width: 26px; height: 26px; }
  .node-hub svg { width: 15px; height: 15px; }
  .node-face, .node-face .av { width: 26px; height: 26px; }
  .node-hub-sm { width: 15px; height: 15px; right: -5px; bottom: -5px; }
  .node-hub-sm svg { width: 9px; height: 9px; }
  /* The chip takes the line under the card rather than a third of its width. */
  .row-wrap.has-more { flex-direction: column; align-items: stretch; }
  .row-wrap.has-more .node-more { align-self: flex-start; margin-left: 34px; }
}
}

/* ---------------------------------------------------------------------------
   The installation's plane.

   Nothing of its own any more, and that is the change: it had a shell — no
   rail, a bar of its own carrying the plane's name, the way back and the
   person — and a shell nobody else wears is a second interface to learn for
   the pages that need it least. It wears the same one now, with the four
   things it administers in the rail instead of the company's six.
   ------------------------------------------------------------------------ */
/* What is waiting, anywhere. Amber, and it is a door into the queue. */
.waits { display: inline-flex; align-items: center; gap: 7px; color: var(--warn-title); }
.waits .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--warn); }
.waits .num { font-size: 13px; }
a.av:hover { color: var(--text-bright); }

/* You, at the foot of the rail. The same disclosure as the company switcher,
   opening upwards because there is nothing below it. */
.me { position: relative; }
.me > summary {
  display: flex; align-items: center; gap: 9px; padding: 8px;
  border-radius: var(--r-nested); cursor: pointer; list-style: none;
  font-size: 13px; color: var(--text-3);
}
.me-who { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.25; }
.me-role {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.me > summary::-webkit-details-marker { display: none; }
.me > summary:hover { background: var(--hover); color: var(--text-bright); }
.me[open] > summary { background: var(--panel-3); color: var(--text-bright); }
.me-name { font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.me-dots { width: 14px; height: 14px; flex: none; color: var(--muted-2); }
/* The light goes between the name and the dots, and it is the only thing in
   the rail that is not about the reader: it says whether this page is still
   hearing from the fleet.
   Its word is dropped here and the dot kept. "reconnecting" is nine
   characters of a 216px column, and it pushed the reader's own name out of
   its own row — measured at 216px. The colour carries it: azure pulsing is
   hearing, amber still is not, and the title says which in words. */
/* By id, because the script rewrites this element's class on every state
   change and a rule hung off the class stops applying halfway through a
   session. */
.rail #live { margin-left: auto; gap: 0; font-size: 0; }
.me-panel {
  position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 30; min-width: 236px;
  display: flex; flex-direction: column; gap: 1px; padding: 6px;
  background: var(--panel-3); border: 1px solid var(--line-strong); border-radius: var(--r-panel);
  box-shadow: 0 20px 44px rgb(0 0 0 / .55);
}
.me-panel a, .me-panel .link {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border-radius: var(--r-small); font-size: 13.5px; text-align: left; color: var(--text-2);
}
.me-panel a:hover, .me-panel .link:hover { background: var(--hover); color: var(--text-bright); }
.me-panel a.on { background: var(--hover); color: var(--text-bright); }
.me-panel form { margin: 5px 0 0; padding-top: 5px; border-top: 1px solid var(--line); }
.me-panel .link { width: 100%; font-family: inherit; font-weight: 400; }
.me-panel .me-build { margin: 0; padding: 6px 10px 2px; font-size: 11px; color: var(--muted-2); }

/* The company switcher. A disclosure in the bar: the name of the company this
   page is about, and a panel of the others.

   Absolutely positioned so that opening it moves nothing — the bar wraps at
   narrow widths, and a panel in the flow would push the strip onto a second
   line the moment it opened. Above the dock's z-index for the same reason the
   dock is above the bar: whatever was opened last is what the reader is
   looking at. */
.switch { position: relative; flex: none; }
/* The bar is the panel's frame at narrow widths — see the media query at the
   end of this block. */

.switch > summary {
  display: flex; align-items: center; gap: 9px; padding: 7px 8px;
  border-radius: var(--r-nested); cursor: pointer; list-style: none;
  color: var(--text-2); min-width: 0;
}
.switch-who { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.switch-name { font-size: 14px; font-weight: 600; letter-spacing: -.01em; color: var(--text-bright);
               overflow: hidden; text-overflow: ellipsis; }
/* Under the name: how many there are when you are standing in all of them, and
   the name of the software when you are standing in one. The vendor's name
   belongs here — small, under the business's own — and nowhere else. */
.switch-sub { font-family: var(--mono); font-size: 9.5px; color: var(--muted-3); }
.switch > summary::-webkit-details-marker { display: none; }
.switch > summary:hover { background: var(--hover); color: var(--text-bright); }
.switch[open] > summary { background: var(--panel-3); color: var(--text-bright); }
.switch-chev { width: 12px; height: 12px; flex: none; color: var(--muted-2); }
.switch-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; min-width: 330px;
  display: flex; flex-direction: column; gap: 1px; padding: 6px;
  background: var(--panel-3); border: 1px solid var(--line-strong); border-radius: var(--r-panel);
  box-shadow: 0 20px 44px rgb(0 0 0 / .55);
}
.switch-head {
  margin: 0; padding: 6px 9px 7px; font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted-2);
}
.switch-row {
  display: flex; align-items: baseline; gap: 14px; padding: 8px 9px;
  border-radius: var(--r-small); font-size: 13.5px; color: var(--text-2);
  white-space: nowrap;
}
.switch-row:hover { background: var(--hover); color: var(--text-bright); }
.switch-row.on { background: var(--hover); color: var(--text-bright); }
.switch-row .mono { margin-left: auto; font-size: 10px; color: var(--muted-2); white-space: nowrap; }
/* How many decisions wait in a company you are not looking at. Amber, because
   it is the product's one word for "this is waiting for you", and it sits
   before the counts so the eye finds it in a column of grey. */
.switch-waiting {
  flex: none; padding: 0 5px; border-radius: var(--r-tag);
  background: var(--warn); color: var(--accent-ink);
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
}
/* The mark on a row of Knowledge: what this thing *is*, and therefore what it
   does. Amber, because a rule is the one kind that can stop work and amber is
   this product's word for "this reaches you"; grey when it is not applying,
   which is the same row with its power taken away. Reference material, when
   there is any, wears this box in grey permanently — one family, two members,
   and no vocabulary to learn. */
.mark-rule {
  display: inline-block; padding: 1px 6px; border-radius: var(--r-tag);
  border: 1px solid var(--warn-tint-border); background: var(--warn-tint-bg);
  font-family: var(--mono); font-size: 9px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--warn-title);
}
.mark-rule.off { border-color: var(--line-strong); background: transparent; color: var(--muted-2); }
/* One line under a page, saying what a mark in it means. */
.legend { display: flex; align-items: baseline; gap: 9px; margin: 0; font-size: 12.5px; color: var(--muted); }

/* The row that opens the form. It is a <button> — it opens something rather
   than going somewhere — so it has to be told to look like the links it sits
   with: a browser button brings its own ground, border, font and centring. */
button.switch-row {
  width: 100%; border: 0; background: none; font: inherit; text-align: left; cursor: pointer;
}
/* The door to where companies are drawn, under the list of them: not a place
   to stand, so set off from them by a rule. */
.switch-net { color: var(--muted); border-top: 1px solid var(--line); margin-top: 5px; padding-top: 12px; }
.switch-none { margin: 0; padding: 8px 9px; font-size: 13px; color: var(--muted-2); }

/* On a phone the panel is pinned to the window rather than to the control:
   the control sits a third of the way across a 390px screen, and a panel wide
   enough to hold a company's counts would open off the right edge of it. The
   counts go under the name here for the same reason — one line of two facts
   fits a desk and not a thumb. */
@media (max-width: 46rem) {
  /* Anchored to the bar rather than to the control inside it, so that "under
     the bar, as wide as the window" is one rule and not an arithmetic guess at
     how many lines the bar wrapped onto. */
  .rail { position: relative; }
  .switch { position: static; }
  .switch-panel { top: 100%; left: 10px; right: 10px; min-width: 0; }
  .switch-row { flex-wrap: wrap; gap: 2px 14px; }
  .switch-row .mono { margin-left: 0; flex-basis: 100%; white-space: normal; }
}

/* The freshness light. It pulses while the stream is live and goes still and
   amber when it is not — a page that has quietly stopped updating is worse
   than one that says so. */
.live { display: inline-flex; align-items: center; gap: 6px; }
.live::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  animation: musterPulse 1.6s ease-in-out infinite;
}
.live.off { color: var(--warn); }
.live.off::before { background: var(--warn); animation: none; }


/* ---------------------------------------------------------------------------
   Conditions. Observed, never desired.
   --------------------------------------------------------------------------- */
.cond {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font-family: var(--mono); font-size: 10px; letter-spacing: .02em;
  padding: 0 7px; line-height: 1.35;
  border-radius: var(--r-tag); border: 1px solid transparent;
  /* Centred on the line rather than sitting on its baseline. A pill is shorter
     than the line box it lives in — 15.5px against the 20-odd a table row's own
     type makes — so baseline alignment drops it about a pixel and a half below
     everything beside it, which is the amount that makes a row look untidy. */
  vertical-align: middle;
}
.cond::before { content: ""; width: 4px; height: 4px; border-radius: 50%; flex: none; background: currentColor; }

.cond-waiting { color: var(--warn); border-color: rgba(232,163,61,.4); background: rgba(232,163,61,.12); }
.cond-waiting::before { animation: musterPulse 1.6s ease-in-out infinite; }
.cond-running { color: var(--accent); border-color: rgba(107,166,255,.35); background: rgba(107,166,255,.1); }
.cond-running::before { animation: musterPulse 1.6s ease-in-out infinite; }
.cond-failed  { color: var(--fail); border-color: rgba(226,96,63,.4); background: rgba(226,96,63,.1); }
/* Several pills in one cell, one per account, stacked rather than in a row: a
   runners table with three people on it would otherwise put six words of pill on
   one line and wrap them mid-name. Each is its own line and its own control. */
.stackpills { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.cond-queued, .cond-unknown { color: var(--muted); border-color: var(--line); }
.cond-queued::before, .cond-unknown::before { background: var(--muted-3); }

/* ---------------------------------------------------------------------------
   Surfaces
   --------------------------------------------------------------------------- */
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-panel); padding: 22px 24px; }
.panel > header { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.nested { background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--r-nested); padding: 14px 15px; }

/* A group of rows about one thing, inside a section about several: on a
   machine's page, one directory with its company, its counts and its projects.
 *
 * A hairline and not a box. The rule under the group is the same one the tables
 * use between rows, so the page has one idiom and the eye stops asking why this
 * part is raised. */
.group { border-top: 1px solid var(--line); padding: 18px 0 4px; }
.group:first-of-type { border-top: 0; padding-top: 8px; }
.group > header { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
/* The counts line belongs to its group's header, not to the table's columns. */
.group > .ident { margin: 6px 0 16px; }

details.panel > summary { cursor: pointer; font-size: 13.5px; font-weight: 600; }
details.panel[open] > summary { margin-bottom: 16px; }

/* A fold inside a panel: the mechanism behind a reading, where the reading is
   the row above it — on /me, the machines behind one CLI's answer. No second
   box around it, because it is not a second subject. */
details.fold { margin-top: 16px; }
details.fold > summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--muted); }
details.fold[open] > summary { margin-bottom: 12px; }

/* A warn callout: something is waiting for a person. */
.callout {
  background: var(--warn-tint-bg); border: 1px solid var(--warn-tint-border);
  border-radius: var(--r-panel); padding: 18px 20px; color: var(--warn-text);
}
.callout h2, .callout strong { color: var(--warn-title); }
.callout .big { font-family: var(--serif); font-size: 30px; line-height: 1; color: var(--warn); }
/* A big *number* is not a big word, and it is set in the mono family for one
   reason: Instrument Serif's 1 is a bare stem and its 3 an old-style flat-top,
   so a lone serif numeral beside a sentence with no subject of its own reads as
   a bracket or a stray glyph. The mono numerals carry a flag and a foot. The
   serif stays for the big word: "Nobody." is an answer, not a tally. */
.callout .big.count { font-family: var(--mono); font-size: 26px; }

.flash { border-radius: var(--r-nested); padding: 12px 15px; font-size: 13.5px; }

/* The layout's own two, floated so that saying what just happened never moves
   what is underneath. Bottom right on a desk; above the tab bar on a phone,
   where the bottom of the screen already belongs to the thumb. The confirmation
   fades: it reports something the user did on purpose. The refusal does not
   — nobody has read it yet. Under prefers-reduced-motion the fade goes with
   every other animation and the confirmation stays, which is the safe end of
   that trade. */
.toasts {
  position: fixed; right: 20px; bottom: 20px; z-index: 40;
  display: grid; justify-items: end; gap: 8px;
}
/* Wide enough for what it says and no wider: "Done." in a 420px box reads as a
   panel that failed to load rather than as a remark. */
.toasts .flash {
  margin: 0; width: max-content; max-width: min(420px, calc(100vw - 40px));
  box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}
.toasts .flash-ok { background: var(--panel-2); animation: musterToast 7s both; }
@keyframes musterToast { 0%, 86% { opacity: 1 } 100% { opacity: 0; visibility: hidden } }
@media (max-width: 46rem) {
  .toasts { right: 12px; left: 12px; bottom: 74px; width: auto; }
}
.flash-ok { background: var(--panel-2); border: 1px solid var(--line); }
.flash-bad { background: var(--warn-tint-bg); border: 1px solid var(--warn-tint-border); color: var(--warn-text); }
/* A flash carrying more than one sentence: the lead, then one line per thing
   that is wrong. Paragraphs rather than <br>, because on a narrow screen every
   one of them wraps twice and a run of wrapped lines with no gap reads as one
   paragraph that will not end. Measured at 390px. */
.pieces-stale { display: flex; flex-direction: column; gap: 7px; }
.pieces-stale p { margin: 0; }
.pieces-stale p + p { font-size: 12.5px; opacity: 0.85; }

/* ---------------------------------------------------------------------------
   Controls. No pill buttons on desktop.
   --------------------------------------------------------------------------- */
button, .btn {
  font: inherit; font-size: 13.5px; font-weight: 500; cursor: pointer;
  padding: 8px 14px; border-radius: var(--r-control);
  border: 1px solid var(--line-strong); background: transparent; color: var(--text-2);
}
button:hover, .btn:hover { background: var(--hover); color: var(--text); }
button.primary, .btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600;
}
button.primary:hover, .btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* The one thing in this interface that destroys something with no way back:
   ending a tmux session takes the conversation in it with it. It is outlined
   rather than filled — a solid red button is the loudest thing on a page and
   invites the click it is warning about — and it fills only under the cursor,
   at the moment the click is actually being aimed. */
button.bad {
  color: var(--warn); border-color: var(--warn-tint-border);
}
button.bad:hover {
  background: var(--warn); border-color: var(--warn); color: var(--accent-ink);
}
/* A control whose work is now happening on a machine somewhere, and which will
   not answer until it is done. The dot pulses for the reason every other dot in
   this interface pulses: movement means something is happening now, and its
   absence is what says a thing is still. */
button.busy, .btn.busy { color: var(--muted); cursor: progress; }
button.busy::before {
  content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; margin-right: 6px; vertical-align: middle;
  animation: musterPulse 1.6s ease-in-out infinite;
}
button.link { border: none; background: none; color: var(--accent); padding: 0; font-size: inherit; }
button.link:hover { background: none; color: var(--accent-hover); }
/* A link-weight control that destroys something. Both classes carry the same
   specificity and .link is declared second, so `class="link bad"` came out
   azure — the colour this interface reserves for something moving along
   nicely, on the one control that ends a thing. Naming both puts it back. */
button.link.bad { color: var(--warn); }
button.link.bad:hover { background: none; color: var(--warn-title); }
/* The same amber on a link that is not a button. A row of the machines list
   says "lent to nobody" where it would otherwise offer the door, and that
   sentence is the state rather than the destination: azure would say the link
   is ordinary, and it is not — the machine it belongs to takes no work. */
a.link.bad { color: var(--warn); }
a.link.bad:hover { color: var(--warn-title); }
button:disabled { opacity: .4; cursor: not-allowed; }

input, select, textarea {
  font: inherit; font-size: 13.5px; color: var(--text);
  background: var(--panel-2); border: 1px solid var(--line-strong);
  border-radius: var(--r-control); padding: 9px 12px;
}
input::placeholder, textarea::placeholder { color: var(--muted-2); }
select { background: var(--panel-3); }
textarea { resize: vertical; }
label { font-size: 12.5px; color: var(--muted); }
/* The qualifier a label carries: what the field wants, or where what is typed
   ends up. Dimmer is not enough on its own — at the same size it reads as more
   label. A step down in size separates an aside from the thing it is beside,
   and lets it stay on the line the eye is already on. */
label > .dim { font-size: 11px; }

/* A segmented control: one border, dividers inside. The topic page calls its
   history switch .tabs, which is the same control doing the same job. */
/* align-self, because main is a flex column and a column stretches its
   children across: inline-flex asks for the width of its words and the parent
   was overruling it, so a segment of four words spanned 1200px. */
.seg, .tabs { display: inline-flex; align-self: flex-start; border: 1px solid var(--line-strong); border-radius: var(--r-control); overflow: hidden; }
.seg > *, .tabs > * {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; font-size: 13px; color: var(--muted); background: transparent;
  border: none; border-left: 1px solid var(--line-strong); border-radius: 0;
}
.seg > *:first-child, .tabs > *:first-child { border-left: none; }
.seg > .on, .tabs > .on { background: var(--panel-3); color: var(--text-bright); font-weight: 600; }

/* A tab on its own, outside a segmented row: Knowledge's filters and a page's
   own switches. The design draws them as separate chips rather than as one
   joined control, and two templates were committed using the class with no
   rule to match — they drew as body text with a mono count beside them. */
.tab {
  display: inline-flex; align-items: baseline; gap: 7px; padding: 5px 10px;
  border-radius: var(--r-control); font-size: 13px; color: var(--muted);
}
.tab:hover { background: var(--hover); color: var(--text-2); }
.tab.on { background: var(--panel-3); color: var(--text-bright); font-weight: 600; }
.tab .mono { font-size: 10px; color: var(--muted-3); }
.tab.on .mono { color: var(--muted); }

form.stack { display: flex; flex-direction: column; gap: 10px; max-width: 620px; }
form.inline { display: inline; }
.check { display: flex; align-items: center; gap: 7px; font-size: 13px; }
.hint { font-size: 12.5px; color: var(--muted); margin: 4px 0 0; }

/* What an agent recommends, and why. Two lines: the recommendation is the part
   somebody acts on, so it is the line that reads first and the reason is under
   it in the colour of everything else that is context.

   The azure rule holds — azure is "this is moving" — and a recommendation is
   the moving part of a line that is otherwise waiting. */
/* The controls of one task, behind a disclosure: the plan is a list somebody
   runs their eye down, and five controls under every row hides the shape of
   the work inside its own machinery. */
.acts-of { margin-top: 12px; }
.acts-of > summary { cursor: pointer; list-style: none; }
.acts-of > summary::-webkit-details-marker { display: none; }
.acts-of > summary:hover { color: var(--text-2); }
.acts-of[open] > summary { margin-bottom: 10px; }

/* A list with no bullets: responsibilities, skills, the repositories somebody
   works on. They are lists, so they are <ul>; a bullet in a 240px column beside
   a page is a character of noise per line. */
ul.plain { margin: 8px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
ul.plain li { font-size: 13px; color: var(--text-2); }
/* A labelled block beside a page: the label is a `.lab` and the block holds
   one kind of thing. `.aside-block` and not `.card` — `.card` is the design
   kit's word and does not exist in this stylesheet, so four sections drawn as
   cards rendered as bare text on the page's ground. */
.aside-block {
  display: flex; flex-direction: column; gap: 2px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--panel);
}
/* The mark beside a person's own name, at the size of the name rather than of
   a row: 44px, centred on the two lines it stands beside. `.head` aligns its
   children to the bottom — which is right for a title and its subtitle and
   wrong for a face. */
.head .av.big { width: 44px; height: 44px; border-radius: 12px; font-size: 15px; align-self: center; }
/* A facts list inside a column beside the page, rather than across it. */
.facts.tight { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 0; }
.facts.tight > div { display: flex; align-items: baseline; gap: 10px; }
.facts.tight dt { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-3); }
.facts.tight dd { margin: 0 0 0 auto; font-size: 12.5px; color: var(--text-2); }
/* A row of counts across the top of a page: how much work is in motion, how
   much is finished, how much is stuck. A number with a word over it and a word
   under it, which is how a measurement is read.

   Called `counts` and not `tiles`: `.tile` is already the two-letter mark a
   company or an agent is drawn as, 26px square — and a second meaning for one
   class name is the later rule winning silently, which is what happened here
   the first time this was written. */
.counts { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; }
.count {
  display: flex; flex-direction: column; gap: 6px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--panel);
}
.count .num { font-family: var(--serif); font-size: 30px; line-height: 1; color: var(--text-bright); }
.count .num.warn { color: var(--warn-title); }
.count .aside { color: var(--muted-3); }

/* One mission, as a row of a list. The same shape as a person in a chart —
   a mark, two lines, a count and a state — because a list of missions is read
   the same way: down the left edge, one line at a time. */
.mission-row {
  display: flex; align-items: center; gap: 12px; padding: 11px 12px;
  border: 1px solid var(--line); border-radius: var(--r-nested);
  background: var(--panel); color: var(--text);
}
.mission-row:hover { border-color: rgba(107, 166, 255, .45); }
.mission-row .member-name { white-space: normal; }

/* A row of people with two facts at its end: who is on this, and when it
   started. Not a panel — a panel is a box with a heading, and this is one line
   of the page's own head.

   `.crew` and not `.band`, which the machine page has owned since it was
   written: `section.band` is the seam between two of its sections, 46px of
   margin and a hairline. Two meanings for one word, and the machine page's
   two sections came out laid as flex rows on a panel ground — the fourth
   collision of the day, after `.compose`, `.tile` and `.palette`, and every
   one of them silent. */
.crew {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: var(--r-panel);
  background: var(--panel);
}
.crew .member { padding: 4px 6px; }

/* The objective, at the width of a sentence rather than of the window. */
.objective { display: flex; flex-direction: column; gap: 6px; }
.objective p { margin: 0; max-width: 62ch; font-size: 15px; line-height: 1.5; color: var(--text-2); }

/* A fact worth a word beside a state: "Priority high". Quieter than a pill,
   because a pill in this product means an observed state and this is a
   setting. */
.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
  border: 1px solid var(--line-strong); border-radius: var(--r-tag);
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.chip b { color: var(--text-2); font-weight: 500; }

/* ---------------------------------------------------------------------------
   The feed: what the company did, by day.
   --------------------------------------------------------------------------- */
.feed { display: flex; flex-direction: column; gap: 0; }
.feed > header {
  display: flex; align-items: baseline; gap: 10px; padding: 0 0 8px;
  border-bottom: 1px solid var(--line);
}
/* One line of it. A grid and not a flex row: the time and the tile are fixed
   columns, so every sentence on the page starts at the same left edge — which
   is what makes a feed scannable and a row of flex items does not. */
.did {
  display: grid; grid-template-columns: 46px 26px minmax(0, 1fr) auto; gap: 12px;
  align-items: start; padding: 11px 0; border-bottom: 1px solid var(--line-soft);
}
.did:last-child { border-bottom: 0; }
.did-at { font-size: 11px; color: var(--muted-3); padding-top: 4px; }
.did .av { width: 26px; height: 26px; }
.did-what { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.did-line { font-size: 13.5px; color: var(--text-2); }
.did-line b { color: var(--text-bright); font-weight: 600; }
/* What they are, in the same mono capitals every role in this product wears. */
.did-role {
  font-family: var(--mono); font-size: 9px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted-3);
}
.did-object { color: var(--text-bright); }
.did-object:hover { color: var(--accent-hover); }
.did-said { font-size: 13px; color: var(--muted); }
.did-where { font-size: 10px; color: var(--muted-3); }
.did-state { padding-top: 3px; }
/* An agent's own feed says the verb and not the face: a feed on one person's
   page does not need to name them on every line. Written here, after .did,
   because a grid-template-columns declared five hundred lines earlier is one
   the later rule silently overrules — which is what happened, and the middle
   column came out one word wide. */
.did-mine { grid-template-columns: 46px minmax(0, 1fr) auto; }

/* ---------------------------------------------------------------------------
   The chart: a company's people, drawn as its shape.
   --------------------------------------------------------------------------- */
/* The tree. One markup, two pictures: on a desk every node hangs from the one
   above by a stem and a bracket, drawn by the node's own ::before and ::after
   (the classic CSS chart: each child draws half the bar either side of its
   stem, the first and last drop their outer half), so the lines follow however
   many children there are and nothing is measured. On a phone the same nodes
   stack as an outline, indented under a rule — see the media query below. */
.tree { overflow-x: auto; padding-bottom: 4px; }
.tree-node {
  position: relative; display: flex; flex-direction: column; align-items: center;
  padding: 20px 8px 0; min-width: 0;
}
.tree-root { padding-top: 0; min-width: max-content; }
.tree-row { display: flex; align-items: flex-start; width: 100%; position: relative; padding-top: 20px; }
/* The stem down from the parent. */
.tree-row::before {
  content: ""; position: absolute; top: 0; left: 50%; width: 1px; height: 20px;
  background: var(--line-strong);
}
.tree-row > .tree-node { flex: 1 1 0; }
.tree-row > .tree-node::before, .tree-row > .tree-node::after {
  content: ""; position: absolute; top: 0; right: 50%; width: 50%; height: 20px;
  border-top: 1px solid var(--line-strong);
}
.tree-row > .tree-node::after { right: auto; left: 50%; border-left: 1px solid var(--line-strong); }
.tree-row > .tree-node:first-child::before, .tree-row > .tree-node:last-child::after { border-top: 0; }
.tree-row > .tree-node:last-child::before { border-right: 1px solid var(--line-strong); border-top-right-radius: 8px; }
.tree-row > .tree-node:first-child::after { border-top-left-radius: 8px; }
.tree-row > .tree-node:only-child::before { display: none; }
.tree-row > .tree-node:only-child::after { border-top: 0; border-radius: 0; }
.tree-row > .tree-node > .member { flex: none; }

.member.exec {
  min-width: 240px; padding: 10px 12px; border: 1px solid rgba(107,166,255,.35);
  background: var(--panel); border-radius: var(--r-panel);
}
.member.exec .member-name { font-size: 14.5px; }

.dept {
  display: flex; flex-direction: column; gap: 2px; width: 100%; min-width: 220px; max-width: 340px; box-sizing: border-box;
  border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--panel);
  padding: 4px 6px 6px;
}
.dept-name { display: flex; align-items: baseline; gap: 8px; padding: 7px 8px 7px; border-bottom: 1px solid var(--line-soft); margin-bottom: 2px; }
.dept-name h2 { margin: 0; }
/* The members sit under the head, indented to the head's name: the card says
   who leads before it says who else. */
.dept-members { padding-left: 18px; }
.dept-members .member { padding-top: 5px; padding-bottom: 5px; }

.shared { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.shared h2 { margin: 0 16px 0 0; }

/* The counts under the page's name: how many, and how they are. */
.team-counts { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 8px 0 0; font-size: 11.5px; color: var(--text-3); }
.team-counts span { display: inline-flex; align-items: center; gap: 6px; }
.team-counts b { font-weight: 500; color: var(--text-bright); }
.team-counts .member-state { margin: 0; }
.team-waiting { color: var(--warn-title); font-size: 11.5px; }

/* The amber button: something is waiting on the reader. */
.btn.warn { color: var(--warn-title); border-color: rgba(232,163,61,.45); background: var(--warn-tint-bg); }
.btn.warn:hover { border-color: var(--warn); color: var(--warn-title); }

/* One company per line, and one act at its end. */
.roster { border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--panel); overflow: hidden; }
.roster-row {
  display: grid; grid-template-columns: minmax(200px, 1.2fr) minmax(0, 1.6fr) 70px minmax(0, 1.4fr) 170px;
  align-items: center; gap: 20px; padding: 10px 16px; border-bottom: 1px solid var(--line-soft);
}
.roster-row:last-child { border-bottom: 0; }
.roster-head { padding-top: 9px; padding-bottom: 9px; font-family: var(--mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted-2); }
.roster-name { display: flex; align-items: center; gap: 10px; color: var(--text); min-width: 0; }
.roster-name .member-name { font-size: 14.5px; font-weight: 500; }
.roster-name:hover .member-name { color: var(--accent-hover); }
.roster-sub { display: none; }
.roster-leads { display: flex; flex-wrap: wrap; gap: 4px 8px; min-width: 0; }
.roster-leads .member { padding: 4px 6px; }
.roster-people { font-size: 13px; }
.roster-now .team-counts { margin: 0; }
.roster-now .team-counts span:first-child { display: none; }
.roster-act { display: flex; justify-content: flex-end; }

@media (max-width: 46rem) {
  /* The outline: each level indented under a rule, the brackets gone. */
  .tree { overflow: visible; }
  .tree-root { min-width: 0; align-items: stretch; }
  .tree-node { align-items: stretch; padding: 10px 0 0; }
  .tree-row { flex-direction: column; padding: 0 0 0 14px; margin-left: 14px; border-left: 1px solid var(--line-strong); width: auto; }
  .tree-row::before, .tree-row > .tree-node::before, .tree-row > .tree-node::after { display: none; }
  .tree-row > .tree-node { flex: none; }
  .member.exec, .dept { min-width: 0; max-width: none; }
  /* Each person a full-width 44px row. The general rule further down grows a
     member's target with a negative margin, which in a stacked card lays one
     row over the next. */
  .tree a.member, .shared a.member { width: 100%; box-sizing: border-box; min-height: 44px; margin-top: 0; margin-bottom: 0; }
  .member.you { align-self: stretch; }

  .roster-head, .roster-leads, .roster-people, .roster-now { display: none; }
  .roster-row { grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding: 10px 12px; }
  .roster-sub { display: block; }
}

/* One person. A row and not a card: a card per person in a card per department
   is a box in a box, and the department is the box. */
.member {
  display: flex; align-items: center; gap: 10px; padding: 7px 8px;
  border-radius: var(--r-nested); color: var(--text);
}
a.member:hover { background: var(--hover); }
.member.you { border: 1px solid var(--line-strong); background: var(--panel-2); }
.member-who { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.member-name { font-size: 13.5px; color: var(--text-bright); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-role {
  font-family: var(--mono); font-size: 9px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* How they are, as a dot. A column of words — working, full, available —
   reads as a status board, and what a chart is for is the shape; the word is
   in the title and the agent's own page says the rest. */
.member-state { width: 6px; height: 6px; border-radius: 50%; flex: none; margin-left: auto; }
.member-state.st-working { background: #62c08f; }
.member-state.st-available { background: var(--muted-3); }
.member-state.st-full { background: var(--warn); }
.member-state.st-paused { background: transparent; box-shadow: inset 0 0 0 1px var(--muted-3); }

/* One line of the file, and every line is this shape: what kind of wait, how
   long, whose work, who holds it — then the ask, the evidence, the
   recommendation, why it is yours, and the acts.

   The border takes the line's own condition, which is the one place in this
   product where a card is coloured by its state: the file is a queue of things
   in different states and the eye sorts them by edge before it reads a word. */
.wait {
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-panel);
  overflow: hidden;
}
.wait-waiting { border-color: var(--warn-tint-border); }
.wait-failed { border-color: rgba(226, 96, 63, .32); }
.wait > header {
  display: flex; align-items: center; gap: 12px; padding: 12px 16px;
  border-bottom: 1px solid var(--line-soft); flex-wrap: wrap;
}
.wait > header .cond { padding: 0; border: none; background: none; }
.wait .kind {
  font-family: var(--mono); font-size: 10px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--warn-title);
}
.wait-failed .kind { color: var(--fail); }
.wait-running .kind, .wait-queued .kind, .wait-unknown .kind { color: var(--muted); }
.wait > header .age { font-family: var(--mono); font-size: 10.5px; color: var(--muted-3); }
/* The path takes whatever the row has left, and it starts from nothing: the
   head row wraps, and a wrapping flex container decides which line an item
   goes on from its *content* width, before any shrinking happens. A path whose
   hypothetical width is the whole lineage therefore pushes the person holding
   the work onto a second line and never gets asked to shrink. Basis nought,
   right-aligned, and it fits by construction. */
.wait > header .where {
  flex: 1 1 0; display: flex; align-items: baseline; justify-content: flex-end; gap: 7px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted-3); min-width: 0;
}
/* The name of the work, at the end of the path. Brighter than the containers
   it hangs off, because it is the one crumb that is about this card and not
   about where the card lives — and dimmer than the headline under it, which is
   the sentence the reader is here to answer.

   It never shrinks and the lineage always does: when the row runs out of room
   it is the company and the mission that can be cut, because they are repeated
   down the whole file, and the name of this piece of work is the one part of
   the path that is only on this card. */
.wait > header .where .lineage {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wait > header .where .what { flex: none; color: var(--text-3); }
.wait > header .where .what::before { content: "› "; color: var(--muted-3); }
/* Whoever is holding the work, at the right end of the head row: a tile with
   two letters and the name beside it, drawn like every other person here. */
.wait .holder { display: inline-flex; align-items: center; gap: 8px; flex: none; font-size: 12.5px; color: var(--text-3); }
.wait .holder .av { width: 22px; height: 22px; border-radius: var(--r-tag); font-size: 9px; }

.wait-body { display: flex; flex-direction: column; gap: 11px; padding: 15px 16px 16px; }
/* The ask is the biggest thing on the card, because it is the thing being
   answered. 17px against the page's 14: one step, and the only one. */
.wait .ask { font-size: 17px; line-height: 1.35; color: var(--text-bright); }
a.ask:hover { color: var(--accent-hover); }
.wait .says { margin: 0; font-size: 13.5px; color: var(--text-2); }
.wait .members { margin: 0; font-size: 12.5px; color: var(--muted); }
.wait .excerpt {
  margin: 0; padding: 11px 13px; background: var(--terminal); border-radius: var(--r-nested);
  font-family: var(--mono); font-size: 11.5px; line-height: 1.6; color: var(--text-3);
  overflow-x: auto;
}
/* Why it is yours. Amber, which is this product's one word for "waiting for
   you", and the only line on the card drawn in it. */
.wait .yours {
  margin: 0; padding-left: 13px; position: relative;
  font-size: 13px; color: var(--warn-text);
}
.wait .yours::before {
  content: ""; position: absolute; left: 0; top: 6px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--warn);
}
/* The acts, in a row. The shared .answers block is a column of full-width
   46px buttons because it was written for a thumb; on a card at a desk that is
   three stacked bars where the design has three buttons, so the row is
   restored here and the column comes back under the media query at the end of
   this file. */
.wait .acts, .wait .answers { display: flex; flex-direction: row; align-items: center; gap: 8px; flex-wrap: wrap; margin: 2px 0 0; }
.wait .answers > button, .wait .answers > .btn {
  width: auto; min-height: 36px; border-radius: var(--r-control); font-size: 13.5px;
}
.wait .answers .row { flex: 1 1 240px; }
.wait .answers .row button, .wait .answers .free { min-height: 36px; border-radius: var(--r-control); }

/* What happens if nobody answers: the one line on a card about the future.
   Azure, the colour of "moving", and quiet — it is information for weighing,
   not a second thing to act on. */
.wait .nudge {
  margin: 0; padding-left: 13px; position: relative;
  font-size: 12.5px; color: var(--muted);
}
.wait .nudge::before {
  content: ""; position: absolute; left: 0; top: 6px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent-deep);
}

.advice { margin: 8px 0 0; padding-left: 13px; position: relative; }
.advice::before {
  content: ""; position: absolute; left: 0; top: 6px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}
.advice-says { margin: 0; font-size: 13.5px; color: var(--text); }
.advice-says strong { color: var(--text-bright); font-weight: 600; }
.advice-why { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }

/* ---------------------------------------------------------------------------
   The folded rest of the file.

   A row and not a card: the whole point of it is that it is one line high, so
   that a reader can see six kinds of waiting at once instead of scrolling
   through forty cards. It opens into the cards themselves — the same ones the
   top of the file draws — because nothing here is a summary of work that lives
   somewhere else.
   ------------------------------------------------------------------------ */
.grouped-head { margin: 22px 0 10px; }
.grouped {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-panel);
  overflow: hidden;
}
/* A <summary> is display: list-item, which is what paints the disclosure
   triangle and what stops it being a flex row. Both are given up on purpose:
   the count at the left is the marker, and it says how many rather than which
   way the triangle points. */
.grouped > summary {
  display: flex; align-items: center; gap: 13px; flex-wrap: wrap;
  padding: 11px 16px; cursor: pointer; list-style: none; font-size: 14px;
}
.grouped > summary::-webkit-details-marker { display: none; }
.grouped > summary:hover { background: var(--panel-2); }
.grouped > summary .cond { padding: 0; border: none; background: none; }
/* How many are folded here. Named `tally` and not `count`, which is already a
   card with a border and a serif number in this file — a row that borrowed it
   drew a box around its own first character. */
.grouped .tally {
  font-size: 15px; line-height: 1; color: var(--text-bright);
  min-width: 22px; text-align: right;
}
.grouped .title { color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grouped .where, .grouped .who {
  font-family: var(--mono); font-size: 10.5px; color: var(--muted-3); flex: none;
}
.grouped[open] > summary { border-bottom: 1px solid var(--line-soft); }
.grouped[open] > .queue { padding: 13px 13px 14px; }


/* ---------------------------------------------------------------------------
   Tables
   --------------------------------------------------------------------------- */
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
th {
  text-align: left; font-family: var(--mono); font-size: 10px; letter-spacing: .09em;
  text-transform: uppercase; font-weight: 400; color: var(--muted-2);
  padding: 0 14px 9px 0; border-bottom: 1px solid var(--line-strong); white-space: nowrap;
}
td { padding: 11px 14px 11px 0; border-bottom: 1px solid var(--line-soft); vertical-align: top; }

/* The cell that names the row, and its link filling it.

   A row is not clickable anywhere in this product, and this is what stands in
   for it. A whole-row handler is not a link, so middle-click, ⌘-click,
   open-in-new-tab and the URL preview on hover all stop working — and when it
   breaks it leaves a table that looks right and does nothing, where a broken
   anchor is still a link. So the anchor takes the cell instead: the negative
   margin pulls it out to the cell's edges and the padding puts the space back
   inside it, which makes the whole cell the target without the row promising
   anything it cannot keep. The tr:hover tint carries on saying "this is one
   row".

   Marked per cell rather than matched on td:first-child > a. Some first cells
   carry a badge beside the link — a project and its "git" — and a block anchor
   there would push it onto its own line, on some rows and not others. */
.lead > a { display: block; margin: -11px -14px -11px 0; padding: 11px 14px 11px 0; }
th:last-child, td:last-child { padding-right: 0; }
tbody tr:hover, table tr:hover { background: rgba(255, 255, 255, .015); }
/* Digits line up under digits, and under their own header. A count set as prose
   is a column somebody reads instead of scanning. And room on the right,
   because the column beside a number is often the one holding that row's
   control: right-aligned, "1" and "ignore" met in the middle of the projects
   table with nothing between them. */
td.num, th.num { font-variant-numeric: tabular-nums; text-align: right; padding-right: 26px; }

/* ---------------------------------------------------------------------------
   The metric strip. Hairlines are the gaps: one border round the group, a 1px
   grid gap over the line colour, cells painted panel.
   --------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   What a company respects — the Settings block at the foot of its page.

   A row is a label, the control, and one mono note under it that says the
   consequence. The control is the thing itself: a field, a switch, a set of
   people, a name. Never a chevron — see the note on the block in company.html.
   ------------------------------------------------------------------------ */
.settings .set-rows { display: flex; flex-direction: column; }
.set-row {
  display: grid; grid-template-columns: 150px 1fr; gap: 4px 20px; align-items: center;
  padding: 14px 0; border-top: 1px solid var(--line-soft);
}
.set-row:first-child { border-top: 0; padding-top: 4px; }
.set-lab { font-size: 13.5px; color: var(--text-3); grid-row: span 2; align-self: start; padding-top: 6px; }
.set-val { min-width: 0; font-size: 14px; color: var(--text); }
.set-val.row { gap: 8px; flex-wrap: nowrap; }
.set-val .grow { min-width: 0; }
.set-note { grid-column: 2; font-size: 10.5px; color: var(--muted-2); }
.set-note a { color: var(--muted); }
.set-note a:hover { color: var(--accent-hover); }
/* A person or a group, as a small tile with a name — the same two-letter tile
   every person in this product is drawn as. Its own class: .chip already means
   two other things in this file. */
.who-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.who-chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 3px 9px 3px 4px;
  border-radius: var(--r-small); background: var(--panel-2); color: var(--text);
  font-size: 13px; white-space: nowrap;
}
.who-chip .av { width: 18px; height: 18px; border-radius: 4px; font-size: 8px; }
a.who-chip { padding-left: 9px; }
a.who-chip:hover { background: var(--panel-3); color: var(--text-bright); }
/* A rule as its switch. The track is the state, the text is the rule, and the
   whole thing is the button: a switch you have to aim at beside a sentence you
   have to read is two targets for one act. */
.set-val.rules { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.switch-rule {
  display: inline-flex; align-items: center; gap: 10px; padding: 4px 8px 4px 4px;
  border: 0; border-radius: var(--r-small); background: none; cursor: pointer;
  font: inherit; font-size: 13.5px; color: var(--muted); text-align: left;
}
.switch-rule:hover { background: var(--panel-2); color: var(--text); }
.switch-rule.on { color: var(--text); }
.switch-rule i {
  position: relative; display: inline-block; flex: none;
  width: 28px; height: 16px; border-radius: 8px; background: var(--panel-3);
  transition: background .15s;
}
.switch-rule i::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--muted-2); transition: transform .15s, background .15s;
}
.switch-rule.on i { background: var(--accent); }
.switch-rule.on i::after { transform: translateX(12px); background: var(--accent-ink); }
.switch-rule.still { cursor: default; }
.switch-rule.still:hover { background: none; }
@media (prefers-reduced-motion: reduce) { .switch-rule i, .switch-rule i::after { transition: none; } }

.metrics {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1px; background: var(--line); border: 1px solid var(--line);
  border-radius: var(--r-panel); overflow: hidden;
}
.metrics > div { background: var(--panel); padding: 18px 20px; display: flex; flex-direction: column; gap: 5px; }
.metrics dt { font-family: var(--mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2); }
.metrics dd { margin: 0; font-family: var(--serif); font-size: 38px; line-height: 1; color: var(--text-bright); font-variant-numeric: tabular-nums; }
.metrics dd small { font-family: var(--sans); font-size: 13px; color: var(--muted); }
.metrics .delta { font-family: var(--mono); font-size: 11.5px; color: var(--accent); }
.metrics .delta.bad { color: var(--warn); }
.metrics .note { font-size: 12.5px; color: var(--muted); }

/* ---------------------------------------------------------------------------
   Charts: bars whose height is the value and whose colour is its magnitude.
   Amber means the day started work late, never "over a limit".
   --------------------------------------------------------------------------- */
.bars { display: flex; align-items: flex-end; gap: 8px; height: 186px; }
.bars > i { flex: 1; display: block; border-radius: 3px 3px 0 0; background: var(--accent-deep); }
.bars > i.lowest { background: var(--accent-deepest); }
.bars > i.mid { background: var(--accent-mid); }
.bars > i.high { background: var(--accent); }
.bars > i.late { background: var(--warn); }
.bar-labels { display: flex; gap: 8px; }
.bar-labels > span { flex: 1; font-family: var(--mono); font-size: 10.5px; color: var(--muted-2); text-align: center; }
.bar-labels > span.late { color: var(--warn); }

/* A share of the fleet. A distribution, so it is never an alarm. */
.share { display: flex; align-items: center; gap: 10px; }
.share .track { flex: 1; height: 5px; border-radius: 999px; background: var(--track); overflow: hidden; }
.share .track > i { display: block; height: 100%; background: var(--accent); }
.share .pct { font-family: var(--mono); font-size: 11px; color: var(--text-2); }

/* Progress. Animated only while the work is live; flat and inert otherwise. */
.progress { height: 3px; border-radius: 999px; background: var(--inert); overflow: hidden; }
.progress.live {
  background: linear-gradient(90deg, var(--accent-deep), var(--accent));
  background-size: 200% 100%; animation: musterSlide 2.2s linear infinite;
}

/* ---------------------------------------------------------------------------
   The queue and the decision
   --------------------------------------------------------------------------- */
.queue { display: flex; flex-direction: column; gap: 10px; }
.qgroup { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.qgroup .cond { padding: 0; border: none; background: none; }
.qgroup .label { font-family: var(--mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2); }

.decision {
  background: var(--panel-2); border: 1px solid var(--warn-tint-border);
  border-radius: 14px; padding: 16px 18px; display: flex; flex-direction: column; gap: 11px;
}
.decision > header { display: flex; align-items: center; gap: 9px; margin: 0; }
.decision .age { font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--warn); }
.decision .id { margin-left: auto; font-family: var(--mono); font-size: 10.5px; color: var(--muted-2); }
.decision .title { font-size: 15.5px; font-weight: 600; line-height: 1.3; color: var(--text-bright); }
.decision .where { font-family: var(--mono); font-size: 10.5px; color: var(--muted-2); }
.decision .ask { font-size: 13.5px; color: var(--text-2); line-height: 1.45; margin: 0; white-space: pre-wrap; }

/* What it is asking, in its own words. */
.excerpt {
  background: var(--terminal); border: 1px solid var(--line); border-radius: 12px;
  padding: 13px 14px; font-family: var(--mono); font-size: 11.5px; line-height: 1.75;
  white-space: pre-wrap; overflow-x: auto; margin: 0;
}
.excerpt .warn { color: var(--warn); }
.excerpt .ok { color: var(--accent); }
.excerpt .dim { color: var(--muted-2); }

.answers { display: flex; flex-direction: column; gap: 7px; }
.answers > button, .answers > .btn {
  display: flex; align-items: center; justify-content: center; width: 100%;
  min-height: 46px; border-radius: 12px; font-size: 14.5px; text-align: center;
}
.answers .row { gap: 8px; }
.answers .row button { min-height: 46px; border-radius: 12px; }
.answers .free { min-height: 46px; }

/* One row per queued decision, when it is not the one being answered. */
.qrow {
  display: flex; align-items: center; gap: 12px; padding: 11px 0;
  border-bottom: 1px solid var(--line-soft); font-size: 13.5px;
}
.qrow .age { font-family: var(--mono); font-size: 11px; color: var(--warn); min-width: 46px; }
.qrow .title { font-weight: 600; }
.qrow .where { font-family: var(--mono); font-size: 10.5px; color: var(--muted-2); }
.qrow .ask { color: var(--muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------------------
   The session terminal
   --------------------------------------------------------------------------- */
.terminal {
  background: var(--terminal); border: 1px solid var(--line); border-radius: var(--r-nested);
  padding: 14px 16px; font-family: var(--mono); font-size: 11.5px; line-height: 1.75;
  white-space: pre; overflow: auto; max-height: 30rem; margin: 0; color: var(--text);
}
.terminal .dim { color: var(--muted-2); }
.terminal .warn { color: var(--warn); }
.terminal .ok { color: var(--accent); }
.caret { animation: musterCaret 1.1s step-end infinite; }

.keys { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
.keys button { font-family: var(--mono); font-size: 11.5px; padding: 6px 10px; border-radius: var(--r-small); }

/* What names a screen and what acts on it, on one line. Both the topic page and
   a pane carry this class. */
.pane-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.pane-head .t { font-weight: 600; }

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

/* ---------------------------------------------------------------------------
   A topic in two columns: what was said, and what is happening.

   They are two halves of one thing — you read the last answer while watching
   what it produced. The columns are *placed*, not reordered: the markup keeps
   the phone's order, where the screen and the box you answer in come first.
   --------------------------------------------------------------------------- */
/* The single column is minmax(0, 1fr) and not the implicit `auto`: a grid track
   sized to its content takes the terminal's max-content width — the widest line
   of an unwrapped capture — and pushes the whole page sideways on a phone. */
.split { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; align-items: start; }
.split > .split-live, .split > .split-history { display: flex; flex-direction: column; gap: 12px; min-width: 0; }

/* The topic's own page splits on the viewport, because that is what bounds it. */
@media (min-width: 64rem) {
  /* The file and the drawing are not halves. The file is the page — a queue
     of cards each carrying a question, its evidence, a recommendation and its
     acts — and the drawing beside it is an instrument you glance at. Two equal
     columns gave the cards 440px, which is where their head rows wrapped and
     their buttons stacked; the drawing lost nothing by being the width of a
     column of desks.

     352px is that width: four desks and their names, measured. */
  .topic-split { grid-template-columns: minmax(0, 1fr) 352px; }
  .topic-split > .split-history { grid-column: 1; grid-row: 1; }
  .topic-split > .split-live { grid-column: 2; grid-row: 1; }
  /* The conversation scrolls in its own box, capped at the terminal's height,
     so the two columns agree on how tall they are and neither drags the page
     down past the other. */
  .topic-split .msgs { max-height: 30rem; overflow: auto; }
}

/* ---------------------------------------------------------------------------
   Facts, laid out as a band of ruled cells
   --------------------------------------------------------------------------- */
/* What a thing is, under what it is called. The machine's host and agent at the
   top of its page, a workspace's path and counts under its name — one line of
   mono, quieter than the name and never competing with it. */
.ident { margin: 6px 0 0; font-family: var(--mono); font-size: 11.5px; color: var(--muted-2); }

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r-panel); overflow: hidden; }
.facts > div { background: var(--panel); padding: 12px 15px; display: flex; flex-direction: column; gap: 4px; }
.facts dt { font-family: var(--mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2); }
.facts dd { margin: 0; font-size: 13.5px; font-weight: 500; }
.facts dd.mono { font-weight: 400; font-family: var(--mono); font-size: 12px; }

/* ---------------------------------------------------------------------------
   Trees: the team chart and the work breakdown are one drawing
   --------------------------------------------------------------------------- */
ul.tree, ul.tree ul { list-style: none; margin: 6px 0; padding-left: 18px; }
ul.tree { padding-left: 0; }
ul.tree ul { border-left: 1px solid var(--line-strong); }
ul.tree li { padding: 5px 0; }
.node { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font-size: 13.5px; }
.node .nm { font-weight: 600; }
.node .ti { font-family: var(--mono); font-size: 10.5px; color: var(--muted-2); }

/* An initial tile, for a company or an agent. `.av` is the design's word for
   the same object and nine templates were committed using it with no rule to
   match — so every face in the product, on the plan, in a chart, on a feed and
   in the reader's own corner, drew as two grey letters with no tile under
   them. Both names, one rule. */
.tile, .av {
  width: 26px; height: 26px; border-radius: var(--r-tag); background: var(--track);
  display: grid; place-items: center; font-size: 11px; font-weight: 600; color: var(--text-2); flex: none;
}
.tile.lg, .av.lg { width: 30px; height: 30px; border-radius: 7px; font-size: 12px; }

/* Machine activity: one segment per running session, plus a little headroom so
   the machine does not read as saturated. Decorative headroom, not a maximum. */
.activity { display: flex; gap: 4px; }
.activity > i { flex: 1; height: 6px; border-radius: 999px; background: var(--track); display: block; }
.activity > i.on { background: var(--accent); }

.chips { display: flex; gap: 6px; flex-wrap: wrap; }

.crumb { font-family: var(--mono); font-size: 11px; color: var(--muted-2); margin: 0 0 8px; }
.crumb a { color: var(--muted); }

/* ---------------------------------------------------------------------------
   Conversation
   --------------------------------------------------------------------------- */
.msg { display: flex; flex-direction: column; gap: 5px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.msg > header { font-family: var(--mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2); }
.msg pre { margin: 0; font-family: var(--sans); font-size: 13.5px; white-space: pre-wrap; color: var(--text-2); }
.msg-user pre { color: var(--text); }

/* ---------------------------------------------------------------------------
   The sign-in page, which has nothing else on it
   --------------------------------------------------------------------------- */
/* A single 392px column, vertically centred, on a page that stays fluid at any
   width. The column is a fixed measure and the page is not: narrowing the
   window moves the column, it does not reflow the reading line. */
body.signin {
  min-height: 100vh; margin: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 40px 20px;
  background: var(--ground);
}
/* padding and gap are reset, not inherited: this column is a <main>, and the
   app's `main` rule gives every page 32px of padding and a 24px gap. Here 392px
   is the column itself and every space below is a stated distance. */
.signin-col {
  width: min(392px, 100%); padding: 0; gap: 0;
  display: flex; flex-direction: column; align-items: stretch;
}
/* The lockup sits top-left of the column, and the title starts 44px below it. */
.signin-col .brand { align-self: flex-start; font-size: 15px; }
.signin-title {
  margin: 44px 0 0; font-family: var(--serif); font-weight: 400;
  font-size: 38px; line-height: 1.05; color: var(--text-bright);
}
.signin-lede { margin: 12px 0 0; color: var(--text-3); font-size: 14px; line-height: 1.55; }
.signin-form { margin: 22px 0 0; display: flex; flex-direction: column; gap: 16px; }

/* One button, full column width, 48px. There is no second way in, so there is
   no divider: the link under it is the same door, asking which account. */
.signin-go {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; height: 48px; padding: 0 16px; border: 0; border-radius: 7px;
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--sans); font-size: 14.5px; font-weight: 600; letter-spacing: -.01em;
  cursor: pointer;
}
.signin-go:hover { background: var(--accent-hover); }
.signin-go svg { display: block; flex: none; }
.signin-other {
  align-self: center; margin-top: -8px; padding: 6px 8px; border: 0; background: none;
  font-family: var(--sans); font-size: 13px; color: var(--muted); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line);
}
.signin-other:hover { color: var(--text-3); }

.asks { padding: 18px 18px 16px; border-radius: 10px; }
.asks h2 {
  margin: 0 0 12px; font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .09em; color: var(--muted);
}
.asks ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; }
.asks li { display: flex; gap: 9px; font-size: 13px; line-height: 1.5; color: var(--text-3); }
.asks-mark { flex: none; color: var(--accent); }
/* The scope as it goes over the wire, next to what it buys. A user who
   wants to check the disclosure against GitHub's consent screen can. */
.asks-scope { font-family: var(--mono); font-size: 11px; color: var(--muted-2); white-space: nowrap; }
.asks-not { color: var(--muted); }
.asks-not .asks-mark { color: var(--muted-2); }

.signin-keep { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--text-3); cursor: pointer; }
.signin-keep input { width: 16px; height: 16px; margin: 1px 0 0; flex: none; accent-color: var(--accent); }

.signin-rule { margin: 24px 0 0; border: 0; border-top: 1px solid var(--line); }
.signin-help { margin: 14px 0 0; font-size: 13px; line-height: 1.5; color: var(--muted); }


/* ---------------------------------------------------------------------------
   The panes. Equal rows, so panes shrink as they multiply rather than the page
   growing; a pane is a window onto a topic and the window is what gets smaller.
   --------------------------------------------------------------------------- */
.panes { display: grid; gap: 12px; grid-auto-rows: minmax(0, 1fr); min-height: 320px; }
.pane {
  display: flex; flex-direction: column; gap: 9px; overflow: hidden;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-panel); padding: 13px 15px;
}
.pane.on { border-color: rgba(107, 166, 255, .45); }
.pane.over { border: 1.5px dashed var(--accent); background: #16203a; }
.pane .pane-head { margin-top: 0; border-bottom: 1px solid var(--line-soft); padding-bottom: 9px; }
.pane .pane-title { font-size: 17px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pane .pane-term { flex: 1; min-height: 84px; font-size: 11px; }
.pane .ask { margin: 0; font-size: 13px; color: var(--text-2); }
.pane .answers > button { min-height: 38px; font-size: 13.5px; border-radius: var(--r-nested); }
.pane-empty {
  margin: auto; text-align: center; color: var(--muted-2);
  font-family: var(--mono); font-size: 11px;
}
.pane-close { font-size: 16px; line-height: 1; }

/* At one or two panes there is room to read; above that the title shrinks and
   the terminal keeps only its last few lines. */
.panes:has(.pane:nth-child(5)) .pane-title { font-size: 14px; }
.panes:has(.pane:nth-child(5)) .pane .pane-term { min-height: 60px; }

/* The bottom tab bar exists only on a phone; the top strip carries the same
   destinations everywhere else. */
.tabbar { display: none; }

/* ---------------------------------------------------------------------------
   The week (/week)
   --------------------------------------------------------------------------- */
/* A section of a document holds its heading off what follows it. `.group` was
   written for a group of rows inside a panel, where the heading is a line of
   the table's own; used as the seam between two accountings of a week it needs
   the same 16px `.panel > header` takes, or the chart sits on the h3's
   baseline. */
section.group > header { margin-bottom: 14px; }

/* ---------------------------------------------------------------------------
   The ⌘K panel
   ---------------------------------------------------------------------------
   A panel over the page: a field at the top and grouped rows under it, one of
   them highlighted as where Enter would go. See templates/palette.html for
   what the rows are and app.js for the keys.

   Nothing here is called `palette`, and the name was not free: `.palette` is
   already a project's grid of the commands it knows how to run, six hundred
   lines above, and two classes with one name is the later one winning
   silently. This panel took `display: grid` and a 12px gap from it before the
   collision was found. So the control is `cmd-panel` and everything in it is
   `cmd-*`, which is also what the product calls it in prose.

   The panel's own rules are written as `dialog.cmd-panel` and not `.cmd-panel`
   because the element also carries `modal`, and the block that styles the
   creation dialogs is `dialog.modal` — one class and one type, which outranks a
   bare class however far down the file it sits. A width that loses to a rule
   six hundred lines above it is a 560px command bar.

   It reuses what the product already has: `.kicker` for the group labels, the
   amber pill and the red count off the rail, `.mono` for the values at the
   right, and the `dialog.modal` backdrop. What it defines are the four things
   nothing else in the stylesheet holds — the panel's own frame at the top of
   the window, the filter field, a row, and the selection.
   --------------------------------------------------------------------------- */

/* Position: fixed rather than the top layer's own centring, and stated in full.
   A modal dialog would be centred vertically by the UA and this one belongs
   near the top of the window — a command bar that opens under the reader's
   eyes and not in the middle of the page — and the same rule is what keeps the
   fallback usable in a browser with no <dialog>, where the panel is revealed
   in the flow instead of raised into the top layer. z-index for that case
   alone: above the dock's 30, above the phone's tab bar at 10, and irrelevant
   the moment showModal has put it in the top layer, where nothing on the page
   can paint over it. */
dialog.cmd-panel {
  position: fixed; inset: 0 0 auto 0; z-index: 40;
  width: min(724px, calc(100vw - 24px));
  margin: 84px auto 0;
  max-height: calc(100vh - 116px);
  padding: 0; overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: var(--r-panel);
  background: var(--panel); color: var(--text);
  /* No shadow: depth in this product is the panel step and a border. */
  flex-direction: column;
}
/* A dialog is display:none until it is open, so the flex column is stated on
   the open state — otherwise `display: flex` would show it on every page of
   the product with no script running at all. */
dialog.cmd-panel[open] { display: flex; }

/* The head, which is the bar this key hangs off drawn a second time: the same
   arrow, the same question shape, and the chip that closes it where the bar
   keeps the chip that opens it. */
.cmd-head { flex: none; display: flex; flex-direction: column; gap: 9px; padding: 17px 22px 16px; }
.cmd-lead { margin: 0; font-size: 13px; color: var(--muted-3); }
.cmd-field { display: flex; align-items: center; gap: 12px; }
.cmd-field > svg { width: 16px; height: 16px; flex: none; color: var(--accent); }
/* The field itself, and it is bigger than the rows it filters: what somebody
   is typing is the largest thing on the panel, which is the difference between
   a command bar and a menu with a search box bolted to it. The caret takes the
   accent, because the design draws it as the one moving thing here. */
.cmd-says {
  flex: 1; min-width: 0; padding: 0; border: 0; background: none;
  font-family: var(--sans); font-size: 17px; line-height: 1.3; color: var(--text-bright);
  caret-color: var(--accent);
}
.cmd-says::placeholder { color: var(--muted-3); }
.cmd-says:focus { outline: none; }
/* The keycap. `.key` is styled in the stylesheet only inside `.wayin`, so the
   chip has to be drawn again here; it is the same chip, to the pixel, because
   two keycaps of different sizes on one screen read as two different kinds of
   thing. */
.cmd-panel .key {
  flex: none; padding: 1px 5px; font-size: 10.5px; color: var(--muted-3);
  border: 1px solid var(--line-strong); border-radius: var(--r-tag);
}

/* The hairline under the head. `.sep` carries no rule of its own in the
   stylesheet — only a margin, inside the person's menu — so it is drawn here.
   Reported rather than fixed in place: the stylesheet is not this control's to
   edit, and every other `.sep` in the product is currently invisible. */
.cmd-panel .sep { flex: none; height: 1px; background: var(--line); }

/* The rows, and the only thing on the panel that scrolls: the head stays where
   it is while the list moves under it, so the caret never leaves the window on
   an installation with the fourth group on the panel. */
.cmd-rows {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
  padding: 6px 12px 12px;
}
.cmd-rows .kicker { padding: 10px 10px 4px; }
.cmd-rows .kicker:first-child { padding-top: 6px; }

/* One row: a mark, a word, and sometimes a value at the right. The same shape
   as a row of the company switcher and a row of the person's menu, because it
   is the same kind of thing — a list of places, in a panel, above the page. */
.cmd-row {
  display: flex; align-items: center; gap: 12px; padding: 8px 10px;
  border-radius: var(--r-small); font-size: 13.5px; color: var(--text-2);
  text-align: left; white-space: nowrap;
}
.cmd-row > svg { width: 15px; height: 15px; flex: none; color: var(--muted-2); }
.cmd-word { overflow: hidden; text-overflow: ellipsis; }
/* What the word means, at the right, in the mono face the switcher already
   puts its values in. Muted-2 and not muted-3: these are words, and muted-3
   does not clear 4.5 against this ground — it is for marks. */
.cmd-hint {
  margin-left: auto; padding-left: 14px; flex: none;
  font-family: var(--mono); font-size: 10.5px; color: var(--muted-2);
  overflow: hidden; text-overflow: ellipsis;
}
/* The sign-out row is a button in a form, because signing out writes. The form
   is a wrapper and nothing else, and the button has to lose everything the
   stylesheet gives a button. */
.cmd-act { margin: 0; display: contents; }
button.cmd-row { width: 100%; border: 0; background: none; font: inherit; font-size: 13.5px; cursor: pointer; }
button.cmd-row:hover { background: var(--hover); color: var(--text-bright); }

.cmd-row:hover { background: var(--hover); color: var(--text-bright); }
/* Where Enter goes. It is not the focus — the caret stays in the field while
   this moves — so it is drawn as the product draws a chosen row anywhere else,
   with the panel's own step up in ground rather than an outline, which would
   claim the focus is here. */
.cmd-row.sel, button.cmd-row.sel { background: var(--panel-3); color: var(--text-bright); }
.cmd-row.sel > svg { color: var(--text-3); }
/* And where you already are, which is a different fact and must not be said
   the same way: the destination you are standing on takes the accent on its
   own glyph and the weight on its word, so that a reader who opens the panel
   on the missions page can see both the row they are on and the row Enter
   would follow. */
.cmd-row.on .cmd-word { color: var(--text-bright); font-weight: 600; }
.cmd-row.on > svg { color: var(--accent); }
/* What is waiting, and what has stopped answering. Both pills exist in the
   rail; both are scoped to it there, so they are drawn again for this panel
   and nowhere else. */
.cmd-panel .badge {
  flex: none; padding: 0 5px; border-radius: var(--r-tag);
  background: var(--warn); color: var(--accent-ink);
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
}

/* ---------------------------------------------------------------------------
   On a phone
   ---------------------------------------------------------------------------
   The panel takes the window, because 724px does not fit in 390 and a command
   bar that has to be pinched is not one. It sits near the top for the same
   reason it does on a desk — the caret is where the eyes already are — and the
   list ends above the home bar with room to spare.

   Nothing is done about the tab bar across the bottom of the window: the top
   layer paints above it whatever its z-index says, which is exactly why this
   is a <dialog> and not a positioned div. The z-index above is only for the
   browser that has no dialog at all.
   --------------------------------------------------------------------------- */
@media (max-width: 46rem) {
  dialog.cmd-panel {
    width: calc(100vw - 16px);
    margin: 10px auto 0;
    max-height: calc(100vh - 20px);
  }
  .cmd-head { padding: 14px 16px 13px; }
  .cmd-rows { padding: 4px 8px 16px; }
  /* A thumb-sized row, and one line each. */
  .cmd-row { padding: 10px; min-height: 44px; }
  /* The gloss goes. It was wrapped under the word at this width and measured
     at 390px it read as a caption for the row *below* it — the eye takes a
     line of small grey type as belonging to the bold line under it, not the
     one above — and it doubled the height of every row, which put four rows
     on the screen instead of nine. The words themselves are the six in the tab
     bar at the bottom of the same screen, so nothing is lost that the reader
     cannot already see. */
  .cmd-hint { display: none; }
  /* The global narrow-screen rule lifts every input to 16px, which is what
     stops a phone zooming when the caret lands in it. Said again because this
     field is 17px on a desk and must not come down to meet it. */
  .cmd-says { font-size: 17px; }
}

/* ---------------------------------------------------------------------------
   Narrow screens. Answering from a phone is the use the product promises, so
   every tappable row is at least 44px and the primary answers are 46.
   --------------------------------------------------------------------------- */
@media (max-width: 46rem) {
  main { padding: 16px 14px 28px; gap: 18px; }
  h1 { font-size: 32px; }

  /* The head stacks, because `min-width: 0` on the growing half means it never
     wanted to: a row of two on a phone shrinks the title's half to whatever is
     left after the pills. Measured on /me: a 174px subtitle beside a 168px
     pill. Below this width the two halves are two lines. */
  .head { flex-direction: column; align-items: flex-start; gap: 10px; }
  .head .grow { width: 100%; }
  h1.mono { font-size: 22px; }

  /* The rail lies down. Same element, same contents, one shape per width: a
     column down the side of a desk, a bar across the top of a phone. Its six
     destinations are hidden here because the tab bar under the thumb is
     carrying them — a phone with navigation at the top and at the bottom is a
     phone with two navigations. */
  body { padding-left: 0; }
  .rail {
    position: static; flex-direction: row; align-items: center; width: auto;
    padding: 10px 14px; gap: 10px;
    border-right: 0; border-bottom: 1px solid var(--line);
  }
  .rail nav { display: none; }
  .brand { min-height: 44px; }
  .corner { margin: 0 0 0 auto; flex-direction: row; align-items: center; gap: 10px; padding-top: 0; border-top: 0; }
  .me > summary { min-height: 44px; }
  /* The tile and the dots, and nothing else. A name and a role beside them
     in a bar that also holds the company switcher is three names in 390px;
     the tile is the reader's own face and the menu behind it says the rest. */
  .me-who { display: none; }
  /* Opening upwards from the top of the page would open off the screen. */
  .me-panel { top: calc(100% + 6px); bottom: auto; right: 0; left: auto; }

  /* A thumb needs 44px, but not by growing the thing it aims at: a state pill
     is a 15px tag whether it is a button or a label, and the pair have to look
     identical because they say the same kind of thing. The pills opt out here
     and buy their target back below, invisibly. */
  button:not(.cond), .btn, input, select, textarea { font-size: 16px; min-height: 44px; }
  /* The same invisible target the header's switch has, at the size a thumb
     actually needs. */
  .cond.switch::after { height: 44px; }
  /* A link-button keeps the type of the text it sits in and gains a thumb's
     height: a link inside a sentence must not become a 16px box, but it must
     still be catchable. Measured at 390px: 16 to 20px tall otherwise, and the
     actions wearing that height are the ones nobody may miss and hit —
     suspend, remove, take it back, disable.

     Grown rather than given a stretched ::after like the pill: a link has no
     border, so growing it moves nothing but the space around the word, and an
     invisible target over a link in a stacked table row would reach into the
     row under it — which is another "remove". */
  button.link, a.link { min-height: 44px; display: inline-flex; align-items: center; font-size: inherit; }
  .keys button { min-height: 44px; }
  .seg, .tabs { width: 100%; }
  .seg > *, .tabs > * { min-height: 44px; flex: 1; justify-content: center; }
  /* A title that opens something is a tap target like any button. */
  .decision .title, .qrow .title, .qrow .btn,
  .panel > header a, .head a { min-height: 44px; display: inline-flex; align-items: center; }
  /* A path, a branch or a machine name must break rather than push the page. */
  .mono, td, .crumb, .facts dd { overflow-wrap: anywhere; }
  /* A control keeps its intrinsic width until told otherwise, and a select
     holding "the whole workspace" is wider than a phone. */
  .row > select, .row > input, .row > button, .row > .btn { max-width: 100%; min-width: 0; }
  select, input, textarea { max-width: 100%; }

  .terminal { max-height: 18rem; font-size: 11px; }
  .qrow { flex-wrap: wrap; gap: 6px 10px; }
  .qrow .ask { flex-basis: 100%; white-space: normal; }

  /* The card's path gets its own row here. On a desk it is basis nought so it
     shares the head row and shrinks; a phone has no room to share, and a
     basis-nought item never wraps — it is placed on the first line and then
     overflows through whatever is beside it. A full-width basis puts it on a
     line of its own, where the lineage can ellipsise and the name of the work
     survives. */
  .wait > header .where { flex-basis: 100%; justify-content: flex-start; }

  /* A fold is a tap target. 43px at 11px of padding, measured, which is one
     short of the forty-four this file asks of everything a thumb reaches. */
  .grouped > summary { min-height: 44px; }

  /* A thumb reaches the bottom of a phone, not the top of it. */
  .tabbar {
    display: flex; position: fixed; inset: auto 0 0 0; z-index: 10;
    background: var(--bar); border-top: 1px solid var(--line); padding: 8px 12px 6px;
  }
  .tabbar a {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px;
    color: var(--muted-2); font-size: 10px; min-height: 44px; text-decoration: none;
  }
  .tabbar a.on { color: var(--accent); font-weight: 600; }
  .tabbar svg { width: 20px; height: 20px; display: block; }
  .tabbar span { display: inline-flex; align-items: center; gap: 4px; }
  .tabbar .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--warn); display: block; }
  /* A grid of panes is a desk arrangement. On a phone the queue is the view. */
  #working-set { display: none; }
  /* The card's acts go back to a column of thumb-sized bars. */
  .wait .acts, .wait .answers { flex-direction: column; align-items: stretch; }
  .wait .answers > button, .wait .answers > .btn,
  .wait .answers .row button, .wait .answers .free { width: 100%; min-height: 46px; border-radius: 12px; }
  .wait .acts form, .wait .acts .btn { width: 100%; }
  .wait .acts .btn { justify-content: center; }
  /* The rail is not down the side here, so nothing has to be cleared. */
  .dock { left: 0; }
  /* The bar floats over the page, so the page ends above it. */
  main { padding-bottom: 84px; }

  /* Pill filters are the one place a 999px radius belongs, and only here. */
  .chips > * { border-radius: 999px; padding: 10px 16px; font-size: 12.5px; }

  /* A table nobody can read sideways becomes one block per row. */
  table.stack thead, table.stack tr:has(th) { display: none; }
  table.stack, table.stack tbody, table.stack tr, table.stack td { display: block; width: 100%; }
  table.stack tr { padding: 12px 0; border-bottom: 1px solid var(--line); }
  table.stack td {
    border: none; padding: 3px 0; white-space: normal;
    display: grid; grid-template-columns: 7rem 1fr; gap: 12px; align-items: baseline;
  }
  table.stack td > * { justify-self: start; }
  table.stack td::before {
    content: attr(data-label); font-family: var(--mono); font-size: 10px;
    letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2);
  }
  /* A cell with nothing in it keeps its label when a table stacks, so an empty
     Detail column printed "DETAIL" five times down a phone with nothing after
     it. The template marks the cell and it goes. */
  table.stack td.blank { display: none; }
  /* Right-aligned digits are for a column of them. Stacked, there is no column
     — the value sits beside its own label — and pushing it to the far edge of a
     phone puts a lonely 0 six centimetres from the word RUNS. */
  table.stack td.num { text-align: left; padding-right: 0; }
  table.stack td:first-child { display: block; font-size: 15px; font-weight: 600; margin-bottom: 4px; }
  /* The title is the link, and it takes the whole title line, because a thumb
     aims at a line and not at the length of somebody's topic name. Measured:
     text-sized it was 90px wide in a 342px row. The vertical padding is given
     back as margin, so the target grows without the card moving. The facts
     under it stay inert: this is the title being the link, not the card being a
     button — a card-sized target would put a page change next to every control
     in the row.

     justify-self, because a stacked cell is a grid and `td > *` is told to sit
     at its start. */
  table.stack .lead > a { display: block; justify-self: stretch; margin: -6px 0; padding: 6px 0; }
  table.stack td:first-child::before { content: none; }
}

/* ---------------------------------------------------------------------------
   Creation dialogs

   Every creation in the product happens here rather than in a disclosure that
   pushes the page around. A <dialog> opened with showModal() is in the top
   layer, which makes the page behind it genuinely inert — not covered, but
   unclickable and unreachable by the keyboard. The backdrop is the visible
   half of that.
   --------------------------------------------------------------------------- */
dialog.modal {
  width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 64px);
  padding: 0; border: 1px solid var(--line-strong); border-radius: var(--r-panel);
  background: var(--panel); color: var(--text);
  /* No shadow anywhere in this product: depth is the panel step and a border. */
  overflow: auto;
}
dialog.modal::backdrop {
  /* Dark enough that the page behind reads as out of play, which is the
     truth: it cannot be clicked while this is open. */
  background: rgba(4, 7, 16, .72);
}
/* The dialog's body, whether it is one form or a container holding several: the
   sign-in has three — start it, send the code back, abandon it — so its body
   is a .stack. */
dialog.modal > form,
dialog.modal > .stack { display: flex; flex-direction: column; gap: 14px; padding: 24px; margin: 0; }
dialog.modal h2 { font-family: var(--serif); font-weight: 400; font-size: 26px; color: var(--text-bright); }
dialog.modal .hint { margin: 0; }
/* The question dialog: the question is the heading, what follows it is the
   consequence in the page's quiet type, and the red button is the act. */
dialog.ask { max-width: 440px; }
dialog.ask .ask-says { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--text-2); }
button.ask-yes, button.ask-yes:hover { background: var(--warn); border-color: var(--warn); color: var(--accent-ink); }
button.ask-yes:hover { background: var(--warn-title); border-color: var(--warn-title); }
/* The row that ends a dialog. Cancel is quiet and sits left of the action, so
   the destructive-looking pair never reads as two equal choices. */
.modal-end { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.modal-end .spacer { flex: 1; }

/* The control that opens one, and the empty state beside it. */
.empty {
  display: flex; flex-direction: column; align-items: flex-start; gap: 12px;
  padding: 22px; border: 1px dashed var(--line-strong); border-radius: var(--r-panel);
  color: var(--text-3);
}
.empty p { margin: 0; max-width: 62ch; }

/* ---------------------------------------------------------------------------
   Arranging the working set: a pane's two halves, the grip that reorders it,
   watching one alone, and filling the screen without asking the browser.
   --------------------------------------------------------------------------- */

/* A pane measures itself: it is one cell of a grid that may hold nine, so its
   own width is the only honest question. */
.pane { container-type: inline-size; }



.pane-grip {
  cursor: grab; color: var(--muted-2); font-size: 13px; line-height: 1;
  padding: 2px 1px; user-select: none;
}
.pane-grip:active { cursor: grabbing; }
.pane-solo { font-size: 13px; line-height: 1; }

/* Watching one alone. The pane keeps its place in the layout, so this hides the
   rest rather than rearranging anything. */
#working-set.solo .panes { grid-template-columns: minmax(0, 1fr) !important; }

/* Filling the screen, from inside the page. Not the browser's fullscreen: this
   keeps the change stream reloading underneath. Everything else on the page is
   hidden rather than scrolled past, and the section is fixed over it. */
body.watching-full { overflow: hidden; }
body.watching-full > *:not(main),
body.watching-full main > *:not(#working-set) { display: none; }
body.watching-full main { padding: 0; }
body.watching-full #working-set {
  position: fixed; inset: 0; z-index: 20; margin: 0; border: none; border-radius: 0;
  display: flex; flex-direction: column; overflow: auto;
}
body.watching-full #working-set .panes { flex: 1; }

/* A sign-in waiting for somebody. It sits under the tool it belongs to: the
   user leaves for a browser and comes back, and what they need has to still
   be on the page. */
.signing {
  display: flex; flex-direction: column; gap: 12px;
  padding: 16px 18px; margin: 4px 0 10px;
  border: 1px solid var(--warn-tint-border); border-radius: var(--r-nested);
  background: var(--warn-tint-bg); color: var(--warn-text);
}
.signing strong { color: var(--warn-title); font-weight: 600; }
.signing p { margin: 0; line-height: 1.55; }
.signing .code {
  font-family: var(--mono); font-size: 14px; letter-spacing: .06em;
  color: var(--warn-title); background: rgba(0, 0, 0, .25);
  padding: 2px 7px; border-radius: var(--r-tag);
}
.signing-out {
  margin: 0; padding: 10px 12px; max-height: 12em; overflow: auto;
  background: var(--terminal); border-radius: var(--r-nested);
  font-family: var(--mono); font-size: 12px; color: var(--text-3); white-space: pre-wrap;
}
.signing-label { font-size: 13px; color: var(--warn-text); }
.signing input[name="reply"] { min-width: 22ch; font-family: var(--mono); }

/* ---------------------------------------------------------------------------
   Consoles

   Everything below exists to keep a console from being mistaken for a topic. It
   has no title, so it is never set in the serif a title takes; it carries a
   command, so it is mono. Amber never appears here — amber means somebody has
   to answer, and a console asks nothing of anybody. The rail down its left is
   grey and inert where a conversation would have azure.
   --------------------------------------------------------------------------- */

/* Two conditions the rest of the interface has no use for.

   cond-ok is a run that finished as it was meant to. Azure, because that is
   what this palette says for "it went the way it should" — there is no green
   here — and perfectly still, because nothing is happening any more.

   cond-quiet is a live pane that has printed nothing for a while. Grey, never
   amber: for a dev server silence is the working state, and amber would be the
   page claiming a person is needed when nobody is. */
.cond-ok { color: var(--accent); border-color: rgba(107,166,255,.35); background: rgba(107,166,255,.1); }
.cond-quiet { color: var(--muted); border-color: var(--line); }
.cond-quiet::before { background: var(--muted-3); }

/* A field that takes the rest of its row. */
.row > .grow { flex: 1; min-width: 0; }

/* The command a pane is running, where a topic would carry its title. */
.console-head { align-items: center; }

/* The grey rail. It is the mark a reader picks up before reading anything: a
   machine, not an interlocutor. */
.console-pane { border-left: 2px solid var(--inert); padding-left: 16px; }

/* The other way in. A web page over a tmux pane must never look like the only
   door, so it is printed whether or not the agent is answering. */
.attach { gap: 8px; font-size: 12.5px; }
.attach-cmd {
  background: var(--terminal); border: 1px solid var(--line); border-radius: var(--r-small);
  padding: 5px 9px; font-size: 12px; color: var(--text-2);
}

.console-status { gap: 8px; }
/* The bar under the pastille moves only while the capture is changing, which is
   the whole of what .live means here. A still bar is not a broken animation. */
.console-live { display: flex; flex-direction: column; gap: 9px; }
.console-end { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.console-end p { margin: 0; max-width: 62ch; font-size: 13px; }

/* The palette: what a project knows how to do. The command is printed and never
   editable — what a press sends is the name above it, and the machine resolves
   that name against what it accepts. */
.palette { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.act {
  display: flex; flex-direction: column; gap: 9px;
  background: var(--panel); border: 1px solid var(--line);
  border-left: 2px solid var(--inert); border-radius: var(--r-nested); padding: 14px 16px;
}
.act > header { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.act-cmd {
  background: var(--terminal); border-radius: var(--r-small); padding: 7px 9px;
  font-size: 11.5px; color: var(--text-2); overflow-x: auto; white-space: pre;
}
.act p { margin: 0; font-size: 12.5px; }

/* ---------------------------------------------------------------------------
   The dock: what is open in a terminal, on every page

   Fixed to the bottom because it is peripheral vision — you keep half an eye on
   a dev server while doing something else — and present everywhere because the
   one left running since Tuesday is only ever found by being in the way.
   Collapsed it is a strip of chips. Open, the strip stays: it is the selector,
   and choosing another pane must not mean closing this one first.
   --------------------------------------------------------------------------- */
.dock {
  /* Clear of the rail, which is fixed to the left edge at desk widths. Written
     after the inset because inset sets left too. */
  position: fixed; inset: auto 0 0 0; left: 216px; z-index: 12;
  display: flex; flex-direction: column;
  background: var(--bar); border-top: 1px solid var(--line-strong);
}
.dock-strip {
  display: flex; align-items: center; gap: 10px; padding: 7px 16px; min-height: 42px;
  overflow: hidden;
}
.dock-toggle {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 5px 10px; font-size: 12px; color: var(--text-2);
}
.dock-num { color: var(--text-bright); font-weight: 600; }
.dock-word { color: var(--muted); }
.dock-chips { display: flex; align-items: center; gap: 8px; overflow-x: auto; scrollbar-width: none; }
/* Pushed to the end of the strip so it never takes width from the chips, which
   are the strip's content. */
.dock-all { flex: none; margin-left: auto; font-size: 11.5px; padding: 6px 0; }
.dock-chips::-webkit-scrollbar { display: none; }

/* One chip per pane. The identifier says c and never t, the command is mono and
   truncated, and the rail on the left is the same inert grey as everywhere else
   a console appears. */
.chip {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  padding: 5px 10px; border-radius: var(--r-small);
  border: 1px solid var(--line); border-left: 2px solid var(--inert);
  background: var(--panel); color: var(--text-2); white-space: nowrap;
}
.chip:hover { background: var(--hover); }
.chip.on { border-color: var(--line-strong); background: var(--panel-3); }
.chip-id { font-size: 11px; color: var(--muted-2); }
.chip-cmd { font-size: 11.5px; color: var(--text-2); }

.dock-body {
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px 16px 16px; border-top: 1px solid var(--line);
  max-height: 60vh; overflow: auto;
}
.dock-body .terminal { max-height: 34vh; }

.dock-type { gap: 8px; }
.dock-type input[name="text"] { flex: 1; min-width: 0; }
/* A page that has a dock ends above it rather than under it. */
body:has(.dock) main { padding-bottom: 64px; }

@media (max-width: 46rem) {
  /* The collapsed strip sits on top of the tab bar rather than over it: the bar
     is 44px of tap targets and a console is not worth covering them. */
  .dock { inset: auto 0 68px 0; }
  body:has(.dock) main { padding-bottom: 140px; }
  /* Open, it is the screen. Above the tab bar's z-index and later in the
     document than it, because navigating away while typing into a shell is not
     a thing anybody means to do. */
  .dock.open { inset: 0; z-index: 30; }
  /* Full screen means the screen fills it: the terminal takes whatever the
     composer and the keys leave, rather than sitting in a band with an empty
     half below. */
  .dock.open .dock-body { flex: 1; max-height: none; }
  .dock.open .console-screen { flex: 1; display: flex; flex-direction: column; min-height: 0; }
  .dock.open .console-live { flex: 1; min-height: 0; }
  .dock.open .dock-body .terminal { flex: 1; max-height: none; }
  body:has(.dock.open) { overflow: hidden; }
  body:has(.dock.open) .tabbar { display: none; }
  .chip { padding: 8px 10px; }
  .dock-toggle { min-height: 36px; }
}

/* Work in progress: a page that promises to refresh itself keeps the promise
   where the reader is looking. The global reduced-motion rule stills these. */
.dots { display: inline-flex; align-items: center; gap: 3px; margin-left: 7px; vertical-align: middle; }
.dots i { width: 4px; height: 4px; border-radius: 999px; background: currentColor; animation: musterDots 1.4s infinite; }
.dots i:nth-child(2) { animation-delay: .2s; }
.dots i:nth-child(3) { animation-delay: .4s; }

/* An OAuth URL is four hundred characters with no space in it. Left alone it
   sets the width of the page and everything else scrolls sideways to follow. */
.signing a, .signing-out { overflow-wrap: anywhere; }

/* The power mark inside a switch pill. Sized to the pill's own type and dimmed
   at rest so it reads as an affordance and never as a second state dot — the
   pulsing dot on the left is the state; this is only the invitation. */
.cond.switch .mark { width: 8px; height: 8px; margin-left: 5px; opacity: .55; }
.cond.switch:hover .mark { opacity: 1; }

/* A word where an action would otherwise be — "as pinned" beside a version that
   already matches what this machine's installer would lay down. It exists so
   the column is not empty: an empty cell reads as a control that failed. */
.tag { font-family: var(--mono); font-size: 10px; color: var(--muted-2); }

/* An icon-only control in a table cell. It carries no label, so it needs a
   larger target than its own 13px and a colour that says "pressable" only on
   approach. */
button.icon {
  border: none; background: none; padding: 2px; line-height: 0;
  color: var(--muted-2); cursor: pointer; border-radius: var(--r-small);
}
button.icon:hover { background: var(--hover); color: var(--accent); }
.rfr { width: 13px; height: 13px; display: block; }

/* The offer under a version: what the machine could have instead. Its own line
   rather than beside the number, because it is a different sentence — one says
   what is installed, the other what installing again would get. */
.avail { display: inline-block; margin-top: 2px; font-size: 11px; }

/* The refresh icon, while the machine is doing what it asked for. The mark
   turns rather than being replaced by a word: it is the only content the button
   has, and a control that empties itself mid-press reads as one that broke.
   Under prefers-reduced-motion the turn is disabled with every other animation
   and the accent colour is what remains, which is enough to say "in hand".

   No fill while it turns: the hover fill is an invitation, and the pointer is
   still on the icon after the press. The turning mark is the whole signal. */
button.icon.busy,
button.icon.busy:hover { color: var(--accent); background: none; cursor: progress; }
/* And not the pulsing dot every other busy button gets: here the mark itself
   is turning, and two indicators on one 13px control is one too many. */
button.icon.busy::before { content: none; }
button.icon.busy .rfr { animation: musterSpin 900ms linear infinite; }

/* A countdown that has run out. Amber, because at that point the link is
   somebody's problem rather than a fact about it. */
.expired { color: var(--warn); }

/* ---------------------------------------------------------------------------
   The plan: the workshop seen from above
   ---------------------------------------------------------------------------
   A drawing, not a list. Every company is a floor, every mission a cluster of desks
   on it, every mission a cluster of neighbouring desks, every topic a desk, and
   every agent a figure standing at one. What a list spends a column on — who,
   where, what state — this spends a position on, and the whole fleet fits on
   one screen because a desk is 38px wide.

   TWO LANGUAGES, AND THEY NEVER OVERLAP. This is the rule the drawing lives or
   dies by, and the reason nothing below sets a colour on both a desk and the
   figure over it:

     The DESK's colour is the TOPIC's state.
       amber (and pulsing)  it is waiting for a person to decide something
       azure                it is ready and nobody has taken it yet
       red                  its execution is broken
       dotted grey          nobody is on it at all
       painted, uncoloured  it is in hand and has nothing to report

     The FIGURE's colour is the AGENT's state.
       azure (and swaying)  working
       amber (and still)    blocked — stillness is the signal, so it never sways
       grey                 doing nothing

   So an amber desk under an azure figure is not a contradiction: somebody is
   working, and the thread they are on needs an answer from you. Reading it as
   one state would lose the thing this view exists to show. Every tint is
   currentColor at a fraction, so a desk and a figure recolour from one property
   each.

   MOVEMENT: five animations, and each one means something.
     planPulse   a desk asking for a decision
     planBlink   the dot at a desk's corner — this agent is typing right now
     planSway    a figure at work, 2px
     planPace    a figure walking between its desk and the free one beside it
     planBreathe what a sway becomes when the reader has asked for less motion

   And the guard rail. The global prefers-reduced-motion rule above turns every
   animation off, which here would leave a page where nothing signals anything —
   a still amber desk is indistinguishable from one that has been answered. So
   under reduce this section takes back exactly what it needs: the translations
   go, the pulse and the blink are restored, and the sway becomes a breath of
   opacity in place. Less motion, not less meaning.
   --------------------------------------------------------------------------- */
@keyframes planPulse   { 0%, 100% { opacity: .16 } 50% { opacity: .46 } }
@keyframes planBlink   { 0%, 44% { opacity: 1 } 50%, 94% { opacity: .12 } 100% { opacity: 1 } }
@keyframes planSway    { 0%, 100% { transform: translateX(-1px) } 50% { transform: translateX(1px) } }
@keyframes planPace    { 0%, 34% { transform: translateX(0) } 50%, 84% { transform: translateX(46px) } 100% { transform: translateX(0) } }
@keyframes planBreathe { 0%, 100% { opacity: 1 } 50% { opacity: .5 } }

/* The container. It sizes its own density from its own width rather than the
   viewport's, because its narrowest home is the cockpit's right-hand column on
   a wide screen — a media query would call that case desktop and be wrong. */
.plan {
  container-type: inline-size;
  display: flex; flex-direction: column; gap: 14px; min-width: 0;
}

/* A floor is a company: a band with its name at the top, the people it is not
   currently using at the right of that band, and its services along the bottom.
   Every company the reader works at is on one page here. */
.pl-floor {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-panel); overflow: hidden;
}
.pl-floor-top {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 12px; background: var(--bar); border-bottom: 1px solid var(--line);
}
/* .pl-tenant styled the band's label while a floor was the customer above the
   company: small mono capitals, because it named a container rather than a
   business. The floor is the company now and its name is an `h4.pl-org` — the
   class the company wore one level down, a heading that happens to open a
   page — so nothing here had to be restyled and this rule styled nothing. */
/* Who is on this floor and not at a desk. They are figures like any other and
   read in the same language — grey, standing still — and they sit at the far
   end of the band rather than in the rows, because an idle agent is a fact about
   the company and not about any one piece of its work. */
.pl-idle { margin-left: auto; display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.pl-idle-none { font-family: var(--mono); font-size: 10px; color: var(--muted-2); margin-left: auto; }
.pl-loafer { display: flex; align-items: center; gap: 5px; min-height: 20px; color: inherit; }
.pl-loafer .pl-name { text-align: left; }

.pl-rows { display: flex; flex-direction: column; gap: 16px; padding: 14px 12px; }

/* .pl-row was one company inside a customer's floor. A floor is a company, so
   `.pl-rows` — its padded body — now holds one `.pl-groups` directly, and
   `.pl-org` below is the band's own heading rather than a row's. */
.pl-org {
  margin: 0; font-family: var(--sans); font-size: 12px; font-weight: 600;
  color: var(--text-2); letter-spacing: -.005em;
}
/* The company's name is the way to its page, and it is the one link on this
   drawing that must not be azure: azure here means an agent is working or a
   topic is free. It is a heading that happens to open something, so it looks
   like the heading until the cursor is on it. */
.pl-org > a { color: inherit; }
.pl-org > a:hover { color: var(--text-bright); text-decoration: underline; text-underline-offset: 2px; }
.pl-groups { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }

/* A group is a mission: desks pushed together under one objective. The box is
   what says "these are neighbours"; it carries no state and never takes a
   colour, so it cannot be mistaken for a third language. */
.pl-group {
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: var(--r-nested); padding: 7px 8px 8px;
}
.pl-mission {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted-2);
  max-width: 15rem; overflow-wrap: anywhere;
}
.pl-cells { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }

/* One cell: a figure, the desk it stands at, and the three lines under it. The
   whole thing is one link — the figure and the desk are the same subject, and
   two targets 9px apart is a choice nobody wants to make with a thumb. 46x76 is
   the floor, and 76 is what the five stacked rows come to on their own. */
.pl-cell {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 46px; min-height: 76px; padding: 0; border-radius: var(--r-small);
  color: inherit; text-decoration: none;
}
.pl-cell:hover { background: var(--hover); color: inherit; }

/* The figure. 20px tall: the 18 the head and body come to, and 2 above for the
   cap a lead wears; 9px wide, which is the body, so the same figure drops into a
   cell over a desk and into the band at the top of the floor without a variant.
   Grey by default — an unrecognised state comes out idle, not invisible. */
.pl-fig {
  position: relative; height: 20px; width: 9px;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  color: var(--muted);
}
.pl-head { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.pl-torso { width: 9px; height: 10px; border-radius: 4px 4px 3px 3px; background: currentColor; margin-top: 1px; }
/* A lead's cap: one rounded stroke laid on the head, in the figure's own
   colour. It says who leads, never how they are — the colour is still the
   agent's state and the cap adds nothing to it. */
.pl-cap {
  position: absolute; top: 1px; left: 50%; transform: translateX(-50%);
  width: 8px; height: 2.5px; border-radius: 999px; background: currentColor;
}

.pl-fig-working { color: var(--accent); animation: planSway 3.4s ease-in-out infinite; }
/* Blocked, and therefore still. The stillness is the whole of it: a blocked
   agent that swayed would read as a working one in an unlucky colour. */
.pl-fig-blocked { color: var(--warn); }
.pl-fig-idle { color: var(--muted-3); }
/* Pacing between this desk and the empty one next to it. 46px is one desk plus
   one gap, so the figure arrives somewhere real. It replaces the sway rather
   than adding to it — the walk already says "at work", and a figure that swayed
   while walking would wobble. */
.pl-fig.pl-pacing { animation: planPace 15s ease-in-out infinite; }

/* The desk. 38x18, its identifier written on it, and its colour stated once as
   `color` — the outline is currentColor and the fill is the same colour at a
   sixteenth of nothing, laid in a layer underneath so the identifier stays
   legible over it. */
/* A desk carries the work, so it is the width of a word and not of an
   identifier: 68px against the 38 it was, which is what a two-word label
   needs at 8px mono on two lines. Measured in a 352px column — four desks a
   row still fit, because that is the width the column was set to. */
.pl-desk {
  position: relative; width: 68px; min-height: 26px; flex: none;
  border-radius: var(--r-tag); border: 1px solid currentColor;
  display: grid; place-items: center; padding: 3px 4px; color: var(--muted-3);
}
.pl-desk::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: currentColor; opacity: .16;
}
.pl-id {
  position: relative; font-family: var(--mono); font-size: 8px; font-style: normal;
  letter-spacing: .01em; color: var(--text-2); line-height: 1.25; text-align: center;
  max-width: 60px; overflow-wrap: anywhere;
}

/* Waiting for a person to decide. The one desk that moves. */
.pl-desk-waiting { color: var(--warn); }
.pl-desk-waiting::before { animation: planPulse 1.8s ease-in-out infinite; }
/* Ready, and nobody has taken it. */
.pl-desk-queued { color: var(--accent); }
/* Broken. */
.pl-desk-failed { color: var(--fail); }
/* In hand and reporting nothing: painted, not coloured. A fifth colour for
   "fine" would be one the reader has to learn in order to ignore. The fill is a
   surface rather than a tint of the outline, so it reads as a desk somebody is
   sitting at. */
.pl-desk-running { color: var(--line-strong); }
.pl-desk-running::before { background: var(--panel-3); opacity: 1; }
.pl-desk-running .pl-id { color: var(--text-3); }
/* Out of touch, which is not broken — see the conditions above. Quiet, solid,
   grey; the dashes are reserved for a desk with nobody at it. */
.pl-desk-unknown { color: var(--muted-3); }
.pl-desk-unknown::before { opacity: .08; }
/* Nobody is on it yet. Drawn and empty: dashes and no fill. */
.pl-desk-free { color: var(--muted-3); border-style: dashed; }
.pl-desk-free::before { content: none; }
.pl-desk-free .pl-id { color: var(--muted-2); }

/* Writing, right now. At the corner of the desk rather than on the figure,
   because it is the thread that is gaining a line. */
.pl-typing {
  position: absolute; top: -2px; right: -2px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
  animation: planBlink 1.1s ease-in-out infinite;
}

/* The three lines under a desk: who, what they are, and what they are doing.
   None carries a colour from either language — a state said twice can disagree
   with itself. They wrap rather than truncate: a step cut off at "waiting for
   rev…" is a step nobody can act on. */
.pl-name, .pl-role, .pl-step {
  max-width: 6.5rem; text-align: center; line-height: 1.25; overflow-wrap: anywhere;
}
.pl-name { font-size: 8.5px; color: var(--text-2); }
.pl-role {
  font-family: var(--mono); font-size: 7.3px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted-2);
}
.pl-step { font-size: 8px; color: var(--muted); }

/* The wall: the services this company is running, along the bottom of its floor.
   Each is a monitor on a stand — an outline, the same colour inside at a
   sixteenth, and a foot — followed by its name. */
.pl-wall {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 8px 12px; background: var(--panel-3); border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 11px; color: var(--muted-2);
}
.pl-svc { display: inline-flex; align-items: center; gap: 6px; font-size: 10.5px; }
.pl-mon {
  position: relative; width: 14px; height: 10px; flex: none; margin-bottom: 3px;
  border: 1px solid currentColor; border-radius: 2px; background: none;
}
.pl-mon::before { content: ""; position: absolute; inset: 0; background: currentColor; opacity: .16; }
.pl-mon::after {
  content: ""; position: absolute; left: 50%; bottom: -4px; width: 6px; height: 1.5px;
  transform: translateX(-50%); border-radius: 999px; background: currentColor;
}
.pl-svc-live { color: var(--accent); }
.pl-svc-live:hover { color: var(--accent-hover); }
.pl-svc-building { color: var(--warn); }
/* On the floor. It is a span and not a link on purpose: there is nothing to
   open, and a link that answers with a connection error is worse than a word
   that told you so. */
.pl-svc-down { color: var(--fail); }
.pl-svc .pl-svc-name { color: var(--text-3); }
.pl-svc-down .pl-svc-name { color: var(--muted); }

/* ---------------------------------------------------------------------------
   Tight. One markup, one class: the names go and the shapes and the states
   stay. A plan with no room for words still says where the work is and what is
   wrong with it, which is the whole point of drawing it.
   --------------------------------------------------------------------------- */
.plan.tight .pl-name,
.plan.tight .pl-mission { display: none; }
.plan.tight .pl-cell { min-height: 28px; min-width: 30px; }
.plan.tight .pl-cells { gap: 6px; }
.plan.tight .pl-rows { padding: 10px 8px; gap: 12px; }
.plan.tight .pl-group { padding: 6px; }

/* And the same, unasked, when the plan itself is narrow — the cockpit column on
   a wide screen is the case a viewport query cannot see.

   300px and not 400. The cockpit's column is 352 wide, which was inside the
   old threshold, so the drawing there dropped every name — and a workshop of
   anonymous desks is a seating chart. At 352 a desk is 68px and a name is
   8.5px: two seats a row with their names, measured. Below 300 there is
   genuinely no room for a word and the shapes carry it alone. */
@container (max-width: 300px) {
  .pl-name, .pl-mission { display: none; }
  .pl-cell { min-height: 28px; min-width: 30px; }
  .pl-cells { gap: 6px; }
  .pl-rows { padding: 10px 8px; gap: 12px; }
  .pl-group { padding: 6px; }
}

@media (max-width: 46rem) {
  /* A cell is a tap target on a phone, and 30x28 is what a finger needs of one
     that is a shape rather than a word. It is not grown to 44: enlarging a desk
     would move it away from the desks beside it, and adjacency is the meaning
     here. The gap is what stops a thumb hitting two of them. */
  .pl-cell { min-width: 30px; min-height: 28px; }
  .pl-cells { gap: 9px; }
  .pl-idle { gap: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  /* The two that move something across the screen stay stopped. What comes back
     is the sway, as a breath in place: something at work still has to look
     different from something stopped. Both selectors are named because a pacing
     figure is a working one, and `.pl-fig.pl-pacing` would otherwise
     out-specify the plain class and leave it perfectly still. */
  .pl-fig-working,
  .pl-fig-working.pl-pacing { animation: planBreathe 3.4s ease-in-out infinite !important; }
  /* These two never moved anything, and they are the page's only report on what
     needs a person and what is being written this second. */
  .pl-desk-waiting::before { animation: planPulse 1.8s ease-in-out infinite !important; }
  .pl-typing { animation: planBlink 1.1s ease-in-out infinite !important; }
}

/* ---------------------------------------------------------------------------
   The other plane: the control at the end of the bar
   ---------------------------------------------------------------------------
   Behind this are the machines, the customers, the register of grants and the
   event log — the installation's business, which is another job with another
   blast radius. It is drawn only for somebody who runs the installation, and
   the whole of what this style has to say is "this goes somewhere else".

   Not a fourth link in the strip: three destinations and a fourth word in the
   same row, at the same size and weight, is read as a fourth destination
   however it is coloured, because the eye groups by position long before it
   reads a hue. So it sits off the end of the bar, past the search, in the
   corner where the reader's own name and the freshness light live — the plane
   you can cross into and the person you are standing here as, neither of them a
   place in a list of places.

   It is a control, and it is built like one — the same 32px height as the
   search field it sits beside, so the right-hand end of the bar is one line of
   objects rather than a control and some text. The amber tint, the amber
   hairline and the amber mark are quiet at rest: a bordered chip on the bar's
   own ground, warm where everything around it is cool. Hovering brings the
   hairline up to full amber. Nothing pulses, nothing glows — it is a door.

   Standing inside the plane fills it. Solid amber with the bar's dark ink on
   top is the loudest thing the header can contain, and that is the claim being
   made: you are not on a page of the product, you are in the installation's own
   plane, where a click reaches every company at once. The three destinations
   wear a panel-3 chip when they are current; this says something else, and it
   does not look anything like them on purpose.
   --------------------------------------------------------------------------- */

/* The way out of this product and into the installation's own, at the foot of
   the reader's menu. Amber, because that plane is amber everywhere; a row and
   not a chip, because it is one line of a menu and not a control in the rail —
   a chip above six destinations reads as a seventh. */
.me-panel .plane {
  display: flex; align-items: center; gap: 7px;
  color: var(--warn-title);
}
/* The hint on that row wraps rather than holding the row rigid. It said
   nowrap, and "Admin console ↗ 2 not answering" is wider than a 216px menu:
   the row overflowed the panel to the right, and at narrow widths the panel is
   pinned to the right edge — so the whole row rendered off the screen while
   its box went on taking up space in the list. */
.me-panel .plane .k { white-space: normal; text-align: right; }
.me-panel .plane:hover { background: var(--warn-tint-bg); color: var(--warn-title); }
.me-panel .plane svg { width: 12px; height: 12px; flex: none; color: var(--warn); }
.me-head { margin: 0; padding: 7px 10px 8px; font-size: 13.5px; color: var(--text-bright); }
.me-panel .k { margin-left: auto; font-size: 10px; color: var(--muted-3); }
.me-panel .sep { margin: 5px 4px; }
/* Machines switched on and not answering, beside the reader's own tile. Not
   amber: amber is the other plane's own colour, and it would disappear against
   the row it sits in. It is the failure colour, which is what a machine that
   has stopped answering is. */
.plane-alarm {
  flex: none; padding: 0 5px; border-radius: var(--r-tag);
  background: var(--fail); color: #fff;
  font-family: var(--mono); font-size: 10px; font-weight: 500; line-height: 16px;
}
/* The same door under a thumb, and only under a thumb. The strip is hidden at
   this width and the tab bar is the navigation, so the control in the bar goes
   with it: two doors to one room on a screen this size is one too many, and the
   one a thumb reaches is the bottom one. The amber is held back to 70% rather
   than filled, because a filled amber tab would be the only block of colour on
   the screen; the band goes to the top of the tab, which in a bar on the bottom
   edge is the side the eye reads as the doorway's own. */
@media (max-width: 46rem) {
  /* The door used to be hidden here because the tab bar carried its own copy
     of it. The bar carries five destinations and nothing else now, and the one
     door is a row in the reader's menu — which is reachable at every width, so
     hiding it here hid it entirely. */
  /* A machine that stopped answering, on the tab. The dot beside Cockpit is
     amber and means somebody is waiting for you; this one is not the same news
     and is not the same colour. */
  .tabbar .dot.bad { background: var(--fail); }
}

/* ---------------------------------------------------------------------------
   The control center: the room behind that door
   ---------------------------------------------------------------------------
   Three blocks, in the order a person who runs the installation arrives with
   them: the numbers, then what is broken, then the doors. The doors are last on
   purpose — a lobby that opens with a menu makes the reader answer "is anything
   wrong?" by visiting four pages.
   --------------------------------------------------------------------------- */

/* What will start nothing. A machine, its condition in the fleet's own pill,
   and the sentence. Three columns at the desk so the pills line up and the list
   can be scanned down; one block per machine on a phone, where three columns is
   four words per line. */
.stopped { margin: 0; padding: 0; list-style: none; }
.stopped > li {
  display: grid; grid-template-columns: minmax(7rem, 13rem) max-content 1fr;
  align-items: baseline; gap: 6px 16px;
  padding: 14px 0; border-top: 1px solid var(--line-soft);
}
.stopped > li:first-child { border-top: none; padding-top: 0; }
.stopped .nm { font-size: 14.5px; font-weight: 600; color: var(--text-bright); }
.stopped .why { margin: 0; font-size: 13px; line-height: 1.5; color: var(--text-3); }

/* The all-clear. A panel and not an absence: an empty section and a fleet with
   nothing wrong with it are two different states. */
.allclear { border-color: var(--line-strong); }
.allclear p { margin: 0; max-width: 68ch; font-size: 14px; line-height: 1.55; color: var(--text-3); }
.allclear strong { color: var(--text-bright); font-weight: 600; }

/* The doors. Cards rather than a row of buttons: each one needs a sentence
   saying what is behind it, because "Users" and "Grants" are words this
   product uses in a particular way and a reader who guesses wrong lands
   somewhere with real consequences. The amber edge on hover is the plane's
   colour, which is where every one of these leads.

   280px: below it the line wraps to three and the row stops reading as one
   set. */
.doors { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.door {
  display: flex; flex-direction: column; gap: 7px; color: inherit;
  background: var(--panel); border: 1px solid var(--line);
  border-left: 2px solid var(--inert); border-radius: var(--r-nested);
  padding: 16px 18px;
}
.door:hover { background: var(--panel-2); border-color: var(--line-strong); border-left-color: var(--warn); }
.door .d-name { font-size: 15px; font-weight: 600; color: var(--text-bright); }
.door:hover .d-name { color: var(--warn-title); }
.door .d-note { font-size: 12.5px; line-height: 1.5; color: var(--muted); }
/* The figure sits on the floor of the card, so a row of cards with sentences of
   different lengths still has one line of figures across the bottom. */
.door .d-fig {
  margin-top: auto; padding-top: 6px;
  font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2);
}

@media (max-width: 46rem) {
  .stopped > li { grid-template-columns: 1fr; gap: 7px; }
}

/* ---------------------------------------------------------------------------
   Phone findings, 390px, against the v2 phone artboards.

   Measured with a headless browser on every page the UX dumper writes, at
   390x900, and re-measured against the tree as it stands after the other
   session's changes — all 27 pages still showed every defect below, with the
   same numbers. Nothing here was eyeballed: each rule names the measurement
   that asked for it.

   Line numbers refer to internal/orchestrator/web/static/style.css, because
   five of these defects are caused by rules already in it. They were resolved
   against a 2470-line version of that file; it grew by 366 lines while this
   audit was being written, so if a number does not land on the rule the comment
   names, grep for the selector instead — the selectors are what the argument
   actually rests on.

   Everything is inside the same `46rem` query the stylesheet already uses, so
   that the desk stays exactly as it is: at 1400px, with this file loaded, the
   rail, the topline, the way in, the switch panel, main, the heads, the tile
   and the ⌘K badge all measure to the pixel what they measure without it. The
   one rule that does change the desk sits outside the query at the bottom, and
   says why.
   --------------------------------------------------------------------------- */

@media (max-width: 46rem) {

  /* -------------------------------------------------------------------------
     1. The company switcher opens off the bottom of the screen.

     `.switch-panel` is absolute with `top: 100%`, and line 477 gives `.rail`
     `position: relative` so that "under the bar, as wide as the window" is one
     rule. Line 1611 then sets `position: static` on `.rail` again — same
     selector, same specificity, later in the file, so it wins and line 477 is
     dead. With no positioned ancestor left, `top: 100%` resolves against the
     initial containing block: measured at 390x900 the panel lands at
     y900..1208, the full height of the window below the fold, 370px wide but
     unreachable. The switcher is the only way to change company on a phone.

     Pinned to the window rather than repaired by restoring line 477, because
     `position: fixed` needs no positioned ancestor and cannot be broken again
     by a later rule about the rail — and because a sheet on the bottom edge is
     what SwitchPhone.dc.html draws: `left:0; right:0; bottom:0`, rounded at
     the top, over a scrim. The thumb is at the bottom of the screen, which is
     the argument the tab bar already won.

     `max-height` and its own scroll because a reader with a dozen companies
     must not get a sheet taller than the window; 78vh leaves the bar visible
     above it, so it still reads as a sheet over a page. */
  .switch-panel {
    position: fixed; inset: auto 0 0 0; top: auto;
    max-height: 78vh; overflow-y: auto;
    min-width: 0; padding: 8px 10px calc(16px + env(safe-area-inset-bottom));
    border-width: 1px 0 0; border-radius: 16px 16px 0 0;
    box-shadow: 0 -20px 50px rgb(0 0 0 / .6);
  }
  /* Above the tab bar (z-index 10, line 1663) and the dock, since the sheet is
     the thing just opened and it covers the bottom edge they live on. */
  .switch[open] > .switch-panel { z-index: 41; }

  /* The scrim behind the sheet. It dims the page so the sheet reads as being
     over it, and it takes the tap: app.js closes any open disclosure when a
     click lands outside it, which is the one gesture everybody makes to
     dismiss a sheet. It could not take the tap before that listener existed —
     a scrim that swallowed taps and closed nothing would have covered the one
     control that closes the sheet and trapped the reader inside it. */
  .switch[open]::after {
    content: ""; position: fixed; inset: 0; z-index: 40;
    background: rgb(4 8 18 / .62); pointer-events: auto;
  }

  /* A row of the sheet was 53px already and reads well; only the last one was
     short. Measured: the old "New company" row, 41px on every page. */
  .switch-row { min-height: 44px; align-items: center; }

  /* The summary that opens it measured 43px on all 27 pages — one pixel under,
     which is worth nothing to look at and is still a miss. */
  .switch > summary { min-height: 44px; }

  /* -------------------------------------------------------------------------
     2. The way in overhangs the bar it sits in.

     `.topline` is `height: 52px` (line 305), and the control inside it is
     taller than that on all 27 pages — measured with the webfonts settled,
     which matters here because this defect is made of text metrics:

       cockpit   `.wayin` is a form round an input, 60px in the 52px bar
       the rest  `.wayin` is an `<a>`, 55px in the 52px bar

     The cockpit's 60 is the input taking `min-height: 44px` from line 1630 and
     adding the wrapper's own 7px padding twice. y61..121 against the bar's
     y65..117: it hangs 5px over the bar's top rule and 4px below it, into the
     first line of main, which is the crowding at the top of that screenshot.

     The other 55 is a different cause with the same result. At 390px the bar
     is 271px wide, and after the arrow, the gaps and the ⌘K badge the words
     have 185px — not enough for "What do you want Muster to do?" on one line,
     so it wraps to two and the badge wraps with it, ⌘ over K.

     Two rules, then. The badge goes first: a keyboard hint on a phone names a
     key the reader does not have, and it is the 38px that makes the difference
     between one line and two. Then the wrapper drops its vertical padding and
     takes 44 instead, so the bar holds a thumb's target inside its own 52px
     and nothing below it moves. */
  /* The count and the reader's own tile are in the rail's own row when the
     rail lies down — the same two objects, eleven pixels above. So the bar
     keeps only the thing that is not up there: the one way in, across the
     whole width. Measured at 390: it recovers 74px for the field, which is
     the difference between "What do you want Muster to do?" on one line and
     on two. */
  /* .settabs was here: the settings subjects as a scrolling strip, for the
     width where there is no rail to hold them. Measured at 390 with four
     companies: 669px of strip in a 390px box, scrolling, with the page itself
     not moving. It went with the page. */

  /* The Settings block: label over the control, the field on a row of its
     own, and a switch a thumb can hit. */
  .set-row { grid-template-columns: 1fr; gap: 6px; }
  .set-lab { grid-row: auto; padding-top: 0; }
  .set-note { grid-column: 1; }
  .set-val.row { flex-wrap: wrap; }
  .set-val.row .grow { flex-basis: 100%; }
  .switch-rule { min-height: 44px; }

  .topline-end { display: none; }
  .topline { padding: 0 14px; }

  .wayin .key { display: none; }
  .wayin { min-height: 44px; padding-top: 0; padding-bottom: 0; }
  /* And if a longer prompt ever arrives, it ends in an ellipsis rather than
     folding the bar in half again. */
  .wayin > span, .wayin-says { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* -------------------------------------------------------------------------
     3. What is waiting is a 20px target.

     `.topline-end > a.waits` measured 20px tall, on every one of the 27 pages.
     The waits link is a door into the queue — the comment at line 346 says so
     in those words — and it sits in a 52px bar, so there is room for 44
     without touching the bar's height. It is a word and a dot with no ground
     of its own, so it can simply be as tall as a thumb.

     The bar used to end in a tile with the reader's initials as well. It was
     a second door to the page the rail's foot already opens, drawn with the
     same two letters 20px away on a desk, so it went — and the overlay below
     that once covered it is left to the one tile that still needs it. */
  .topline-end > a.waits { min-height: 44px; display: inline-flex; align-items: center; }

  /* The tile is not grown, it is covered.

     `.av` is a face: a square with a ground and a radius. Both ways of growing
     it were measured and both spoil it: `min-width: 44px` widens whatever
     row it sits in, and `min-height: 44px` alone stretches the tile to 26x44,
     a tall grey pill where a square face had been.

     So the target is an overlay and the face keeps its 26px, which is the same
     trade line 232 makes for a state pill: the shape is the meaning, the
     target is invisible. 44px centred on a 26px tile reaches 9px each side.

     `.did > a.av` is that face, in an event row on Activity. There it is the
     row's only route to the person: measured 26x26, pointing at
     /org/agents/1, with the bold name beside it plain text and not a link.
     And it has its own reason not to be grown — `.did` is a grid whose first
     column is the tile's width, so a 44px tile would push the whole event
     18px right in a 390px row. The overlay reaches over the name, which is
     text and not a second target, so nothing is stolen by it. */
  .did > a.av { position: relative; }
  .did > a.av::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }

  /* -------------------------------------------------------------------------
     4. The reader's menu is six 36px rows.

     `.me-panel > a` measured 36px on every page — Settings, Sign-ins, Coding
     CLIs, Grants, Admin console, Sign out. The panel is a menu that opens over
     the page, so growing its rows moves nothing underneath: measured open at
     390px it runs y60..405, and 44px rows take it to about y60..453 in a 900px
     window. Sign out is in here, and so is the door to the other plane. */
  .me-panel a, .me-panel .link { min-height: 44px; }

  /* -------------------------------------------------------------------------
     5. The cockpit's decision title is a 23px target.

     Line 1650 already says a title that opens something is a tap target, and
     names `.decision .title` and `.qrow .title`. The cockpit rebuilt its cards
     as `.wait` with the title as `a.ask`, which that list never learned:
     measured 23px, twice, on the cockpit — 17px type at 1.35 (line 922). It is
     the largest thing on the page and the one link the page exists for.

     Grown the way line 1724 grows a stacked table's title — padding out,
     margin back — so the target covers the line without moving the card. */
  .wait-body > a.ask {
    display: block; min-height: 44px;
    margin: -8px 0; padding: 8px 0;
  }

  /* -------------------------------------------------------------------------
     6. The folds that change how work runs are 19px.

     `details.defaults > summary.hint` measured 19px on the cockpit ("Runs on
     build-02 / main, normal priority — change either") and 19px on a mission
     ("What can be done with it"). Both are 362px wide, so the row is already
     the width of the column and only the height is missing. The first one is
     how a reader changes where their work runs.

     Grown with padding and not with `display: flex`, which was the first
     attempt and broke the fold: a `<summary>` is `display: list-item`, and that
     is what paints the disclosure triangle. Flex blockifies it, the marker
     stops being drawn, and the row that says "change either" no longer looks
     like something that opens — measured `list-style-type: disclosure-closed`
     still computed, with no triangle on screen. Padding leaves the list-item
     alone. 13px each side takes the measured 19px to 45. */
  summary.hint { padding-top: 13px; padding-bottom: 13px; }

  /* And a panel that is itself a fold. Measured 20px on an agent's page, 312px
     wide — the whole panel width, so again only the height is missing.
     `details.fold > summary` and `.me > summary` already have their 44 (lines
     1618 and 1647); this is the third disclosure and it was not on the list.
     Padding again, for the triangle, and no negative margin to claw the space
     back: line 545 already spaces an open panel's summary from its contents,
     and a fold's own header growing moves nothing but that panel. */
  details.panel > summary { padding-top: 12px; padding-bottom: 12px; }

  /* -------------------------------------------------------------------------
     7. "Everything0" on Knowledge — caused by line 1650.

     Line 1650 gives `.head a` `display: inline-flex` to buy it 44px. The
     Knowledge filter is `<a class="tab on">Everything <span class="mono">0
     </span></a>` inside `.head`, so that rule blockifies it to a flex box, the
     whitespace between the word and its count is dropped as inter-item space,
     and there is no `gap` to put it back. Measured: text right edge 83.0, span
     left edge 83.0, gap 0.0px — it reads as the word "Everything0".

     Phone-only, which is why desk-width review could not have caught it: at
     wider widths the link is inline and the space is a space.

     6px is what `.seg > *` and `.tabs > *` already use (line 673) for exactly
     this pair — a label and its count — which is why the same filter on
     Activity, where it sits inside `.tabs`, reads correctly. `.tab` itself has
     no rule anywhere in the stylesheet. */
  .head a, .panel > header a { gap: 6px; }

  /* -------------------------------------------------------------------------
     8. The console page centres its whole header.

     Line 1855 `.console-head { align-items: center }` is outside every media
     query and later in the file than line 1599, which turns `.head` into a
     column on a phone. At the desk that property centres a row vertically and
     is right; in a column it centres horizontally, and the axis flip was never
     intended. Measured on a console page: the box spans x14..376 while its
     children sit at x176 (the h1), x152 (the pill), x187 (the id), x82 and
     x138 (the two buttons) — five different centres, a ragged stack, and the
     only page in the product whose h1 does not start at x14. */
  .head.console-head { align-items: flex-start; }

  /* -------------------------------------------------------------------------
     9. A stacked cell with two children puts a value in the label column.

     `table.stack td` is a grid of `7rem 1fr` (line 1698) and every child is
     auto-placed, the `::before` label taking the first cell. A cell holding
     more than one element therefore wraps its second child onto the next grid
     row — into the label column.

     Measured on a machine's lending page, the "Set by" cell: `not recorded` at
     x138, which is the value column, and `0s ago` at x14 — the label column,
     124px left, in the same dim mono the labels are set in, where it reads as
     a column heading. Then a `<br>` sends "for a test" to the value column of
     a third row.

     One cell on one page across all 27, so it is scoped by `:has()` rather
     than paid for by every stacked cell in the product: cells with a single
     value keep the two-column grid, which is what makes /me and the missions
     list read as cleanly as they do. With the grid off, the label prints on
     its own line and the cell's contents flow under it as ordinary text. */
  table.stack td:has(> * + *) { display: block; }
  table.stack td:has(> * + *)::before { display: block; margin-bottom: 2px; }

  /* -------------------------------------------------------------------------
     10. Links inside prose, crumbs and stacked cells.

     Measured, all under a thumb: `p.crumb > a` 14px (machines, consoles, a
     machine's lending), `td > a` 19px and `td.mono > a` 19px (the "install"
     links on a machine, "release CI" on Grants), `dd.mono > a` 16px (a console
     page), `td.lead > a` 35px (missions, machines, companies — line 1724 got
     it from 23 to 35 and stopped short), `td.muted > a` 20px, `p.hint > a`
     17px, `span.mono.dim.aside > a` 14px, `h4.pl-org > a` 16px and
     `div.row > h3 > a` 34px (the company names on Team), `a.did-object` 18px
     (Activity), `a.pl-loafer` 20px (the floor), `a.member` 42px (Team) and
     36px (a mission).

     Padding out and margin back, the idiom line 1724 already uses: the target
     grows to 44px and no row moves, because a stacked card that grew 20px per
     cell would be a different page. Not `min-height`, which would space the
     rows out; the box is grown around the words where it has room to be.

     `.link` and `.btn` are left alone — line 1644 already gives them 44px, and
     it works: measured on Users, "give it" and "allow" are 44px rows. */
  .crumb > a,
  table.stack td > a,
  table.stack td.lead > a,
  .facts dd > a,
  h3 > a,
  h4.pl-org > a,
  .hint > a,
  .aside > a,
  .group > header a,
  .stopped .nm,
  a.did-object,
  a.pl-loafer,
  a.member,
  a.member-name,
  /* A link sitting directly in a `.row`, which is a flex line with its items
     centred (line 253): "Ada Lovelace" 21px and "stopped" 21px in a mission's
     task card, "its floor" 20px inside a stacked cell on /me. Across all 27
     pages `.row > a` is those three and the Knowledge filter, which line 1650
     has already taken to 44 — so it and the classes that carry their own
     target are named out rather than shifted by the negative margin. */
  .row > a:not(.btn):not(.link):not(.tab):not(.av):not(.cond) {
    display: inline-flex; align-items: center; min-height: 44px;
    margin-top: -11px; margin-bottom: -11px;
  }
  /* The lead link is told to be a block at line 1724 and stretches the cell;
     keep that and only correct the height, or the title stops being the whole
     line a thumb aims at. */
  table.stack td.lead > a { display: block; margin: -11px 0; padding: 11px 0; }

  /* A pill that is also a link has no invisible target: line 232 buys one
     back for `.cond.switch` only, because that one is a button. Measured 16px:
     `a.cond.cond-warn` on /me ("administers it", where the name is a link)
     ("administers it"). Same trick as the pill's, so the pill keeps its 15px
     shape and the reader gets a thumb's worth of it. */
  a.cond { position: relative; }
  a.cond::after {
    content: ""; position: absolute; left: 0; right: 0;
    top: 50%; height: 44px; transform: translateY(-50%);
  }

  /* -------------------------------------------------------------------------
     11. A checkbox 13px wide.

     Line 1630 gives `input` `min-height: 44px`, which a checkbox takes on its
     height only: measured 13x44 on the integrate page and on an agent's page.
     A tall sliver is not a target, and on the integrate page it is the control
     that chooses which work is being brought together — it sits alone above
     the card with nothing beside it to tap instead. */
  input[type="checkbox"], input[type="radio"] { min-width: 44px; }
  /* The box stays the size it looks: the target is the label beside it, which
     is what a reader aims at anyway. `.check` is already a flex row (line
     663). */
  .check { min-height: 44px; }
  .check > input[type="checkbox"] { min-width: 0; width: 16px; height: 16px; min-height: 0; }

}

/* ---------------------------------------------------------------------------
   Outside the query, and the only rule here that is: a serif 1 is not a
   number.

   Found while reading these pages at 390px, but it is not a width defect — it
   is the same at the desk, so a rule inside the query would fix the phone and
   leave the screen it was drawn on wrong.

   `.count .num` (line 750) is Instrument Serif at 30px, measured. Line 566 of the
   stylesheet already worked this out for the callout and says it in prose:
   Instrument Serif's 1 is a bare stem, so a lone serif numeral reads as a
   bracket or a stray glyph, and `.callout .big.count` was moved to the mono
   family for that reason. The stat tiles rebuilt today did not inherit the
   lesson. At 4x on a repository page, "Decisions waiting" and "Consoles" each
   show a thin amber stroke with no flag and no foot beside an unmistakable
   serif 0; the same stem appears on the Control center for "needs a user" and
   "customers", and on a company for "directories" and "users".

   The serif stays for 0 and for every other digit — this is the one glyph —
   but a family that changes per digit is worse than a family that does not, so
   the block goes to mono the way the callout's did. 26px because Geist Mono
   sets larger on the same body: line 566 made exactly this trade, 30 to 26.

   Left for the lead to take or leave, since it changes the desk. */
.count .num { font-family: var(--mono); font-size: 26px; }
