/* Portal console, the Flow language. A run is a line through machines, and that line (built from the run's own hops
   and moves) is the only thing on a screen that wears a hue. Everything else is ink on paper: hairline rules instead
   of boxes, hierarchy by type, figures set in the text face with tabular digits.

   Colour has two families and they never meet. Providers are cool (teal for AWS, a deep blue for Modal, forest for
   Lambda, olive for Azure), drawn only as lines, meters' keys and chart marks, never as text; attention is warm (red
   for what failed, umber for what needs a look) and always sits beside its word. Any two providers can meet on one
   path, so the four hues were chosen with the dataviz palette validator as all pairs: in light (on #ffffff) the
   closest pair is 18.4 apart (OKLab, x100) to normal vision and 17.4 under simulated colour blindness, in dark (on
   #0f1012) 16.1 and 15.4; each is inside its theme's lightness band, holds 3:1 on --paper and --paper-2, and stands
   15 or more from --bad and --warn. None is a cloud's brand colour (no AWS orange, no Azure blue), and none lies
   between blue and red. A fifth hue cannot clear those floors beside the four, so E2B is ink, dashed, and This
   machine ink, solid.
   A run that ended draws its path in the quiet colours (--pq-*): the same hues mixed toward --edge, less chroma at
   3:1 or more, never paler than the page's hairlines.
   Text tokens hold 4.5:1 on every surface they sit on (--paper, --paper-2, --paper-3, --raised); --edge, a field's
   line and a button's outline, holds 3:1.

   Type has a job per face. Instrument Sans sets every word and every figure (money, durations, percentages, with
   tabular digits where they line up); its condensed cut (--label-stretch) labels: column heads, keys of a list,
   machine names on a path, lanes. Red Hat Mono is only for what a person copies or types: ids, commands, secret
   names, code and logs. Pages add their rules in pages/<name>.css, scoped under .page[data-page="<name>"], and use
   these tokens only. */

@font-face {
  font-family: "Instrument Sans";
  src: url("/static/fonts/instrument-sans-latin-standard-normal.woff2") format("woff2");
  font-weight: 400 700;
  font-stretch: 75% 100%;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Red Hat Mono";
  src: url("/static/fonts/red-hat-mono-latin-wght-normal.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --paper: #ffffff;
  --paper-2: #f6f6f5;
  --paper-3: #ededeb;
  --raised: #ffffff;
  --rule: #e6e6e3;
  --rule-2: #d3d3cf;
  --edge: #858580;
  --ink: #141414;
  --ink-2: #4b4b48;
  --ink-3: #64645f;
  --ink-4: #b6b6b1;
  --bad: #b3261e;
  --warn: #8a5100;
  --p-aws: #2a9cae;
  --p-modal: #025a8d;
  --p-lambda: #146115;
  --p-azure: #8f9407;
  --wash: rgb(20 20 20 / .055);
  --select: rgb(20 20 20 / .1);
  --scrim: rgb(20 20 20 / .26);
  --shadow: 0 1px 0 rgb(0 0 0 / .03), 0 18px 44px -16px rgb(0 0 0 / .24);
  --shadow-side: 1px 0 0 rgb(0 0 0 / .02), 14px 0 36px -18px rgb(0 0 0 / .26);
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #0f1012;
    --paper-2: #17181b;
    --paper-3: #202125;
    --raised: #18191c;
    --rule: #26272b;
    --rule-2: #36373c;
    --edge: #74757b;
    --ink: #ecebe7;
    --ink-2: #b7b6b1;
    --ink-3: #8f8e8a;
    --ink-4: #46474c;
    --bad: #ff7b6e;
    --warn: #e2a440;
    --p-aws: #1aa8b4;
    --p-modal: #2c709f;
    --p-lambda: #34792e;
    --p-azure: #7ba602;
    --wash: rgb(236 235 231 / .07);
    --select: rgb(236 235 231 / .16);
    --scrim: rgb(0 0 0 / .55);
    --shadow: 0 0 0 1px rgb(255 255 255 / .03), 0 18px 44px -16px rgb(0 0 0 / .75);
    --shadow-side: 14px 0 36px -14px rgb(0 0 0 / .7);
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --paper: #0f1012;
  --paper-2: #17181b;
  --paper-3: #202125;
  --raised: #18191c;
  --rule: #26272b;
  --rule-2: #36373c;
  --edge: #74757b;
  --ink: #ecebe7;
  --ink-2: #b7b6b1;
  --ink-3: #8f8e8a;
  --ink-4: #46474c;
  --bad: #ff7b6e;
  --warn: #e2a440;
  --p-aws: #1aa8b4;
  --p-modal: #2c709f;
  --p-lambda: #34792e;
  --p-azure: #7ba602;
  --wash: rgb(236 235 231 / .07);
  --select: rgb(236 235 231 / .16);
  --scrim: rgb(0 0 0 / .55);
  --shadow: 0 0 0 1px rgb(255 255 255 / .03), 0 18px 44px -16px rgb(0 0 0 / .75);
  --shadow-side: 14px 0 36px -14px rgb(0 0 0 / .7);
  color-scheme: dark;
}

:root {
  /* This machine is ink, E2B ink dashed (its lines carry the dash), an unknown provider a grey */
  --p-local: var(--ink-2);
  --p-e2b: var(--ink-2);
  --p-other: var(--edge);
  /* the quiet colours a run that ended draws its path in: each hue mixed toward the control outline, so less chroma
     and never under 3:1 (light 3.2 to 5.3:1 on --paper-2, dark 3.5 to 5.2:1); the lines still tell the providers apart
     (11.2 to normal vision in light, 9.6 in dark) */
  --pq-aws: color-mix(in oklab, var(--p-aws) 60%, var(--edge));
  --pq-modal: color-mix(in oklab, var(--p-modal) 60%, var(--edge));
  --pq-lambda: color-mix(in oklab, var(--p-lambda) 60%, var(--edge));
  --pq-azure: color-mix(in oklab, var(--p-azure) 60%, var(--edge));
  --pq-e2b: var(--edge);
  --pq-local: var(--edge);
  --pq-other: var(--edge);
  /* the names the pages and charts used before the language: the same roles in its tokens */
  --bg: var(--paper);
  --bg-raised: var(--raised);
  --bg-subtle: var(--paper-2);
  --bg-muted: var(--paper-3);
  --border: var(--rule);
  --border-strong: var(--edge);
  --text: var(--ink);
  --text-2: var(--ink-2);
  --text-3: var(--ink-3);
  --accent: var(--ink);
  --accent-hover: var(--ink-2);
  --accent-soft: var(--select);
  --accent-fill: var(--ink);
  --accent-fill-hover: var(--ink-2);
  --on-accent: var(--paper);
  --ok: var(--ink);
  --bad-soft: var(--paper-2);
  /* chart series given no colour (charts.js seriesColor): the four validated hues first, then two inks */
  --series-1: var(--p-aws);
  --series-2: var(--p-modal);
  --series-3: var(--p-lambda);
  --series-4: var(--p-azure);
  --series-5: var(--ink-2);
  --series-6: var(--ink-3);
  --font-sans: "Instrument Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "Red Hat Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --label-stretch: 87%;
  --topbar-h: 48px;
  --nav-w: 208px;
  --rail-w: 56px;
  --gutter: 32px;
  --control-h: 32px;
  /* Corners, one small scale: a control's (a button, a field with a box, a hover or focus wash), what floats over the
     page (a menu, a dialog, a tooltip, a toast, the save bar, the approval card), and a line's round end (a
     provider's key, as the path's caps are round). A circle is 50%; a section, a table, the nav and the sheet have
     no corners: they are not boxes. */
  --radius: 4px;
  --radius-float: 6px;
  --radius-cap: 99px;
  --speed: 140ms;
  --nav-speed: 160ms;
}

/* Notebook palettes keep Portal's typography and layout, with the shared preference's colour family. */
:root[data-palette="midnight"] {
  --paper: #000; --paper-2: #0d0f14; --paper-3: #171b22; --raised: #101319;
  --ink: #eefaf8; --ink-2: #bfcecc; --ink-3: #95aaa8; --rule: #242e30; --rule-2: #3c4b4e; --edge: #7d979a;
}
:root[data-palette="sepia"] {
  --paper: #f8f0df; --paper-2: #eee3cf; --paper-3: #e7d7bd; --raised: #fff8e9;
  --ink: #342a21; --ink-2: #635443; --ink-3: #75634e; --rule: #d8c9af; --rule-2: #bba888; --edge: #958064;
}
:root[data-palette="nord"] {
  --paper: #2e3440; --paper-2: #353d4b; --paper-3: #434c5e; --raised: #3b4252;
  --ink: #eceff4; --ink-2: #d8dee9; --ink-3: #b8c6db; --rule: #4c566a; --rule-2: #697a94; --edge: #9baec7;
}
:root[data-palette="contrast"], :root[data-high-contrast="true"][data-theme="dark"] {
  --paper: #000; --paper-2: #111; --paper-3: #222; --raised: #111;
  --ink: #fff; --ink-2: #fff; --ink-3: #eee; --rule: #777; --rule-2: #aaa; --edge: #ddd;
}
:root[data-high-contrast="true"]:not([data-theme="dark"]) {
  --paper: #fff; --paper-2: #f5f5f5; --paper-3: #eee; --raised: #fff;
  --ink: #000; --ink-2: #111; --ink-3: #222; --rule: #777; --rule-2: #555; --edge: #333;
}
@media (prefers-color-scheme: dark) {
  :root[data-high-contrast="true"]:not([data-theme="light"]) {
    --paper: #000; --paper-2: #111; --paper-3: #222; --raised: #111;
    --ink: #fff; --ink-2: #fff; --ink-3: #eee; --rule: #777; --rule-2: #aaa; --edge: #ddd;
  }
}
/* ---------------------------------------------------------------- base */

/* Zoom enlarges all text together, including labels and fixed-size chart captions. Responsive breakpoints follow. */
html[data-text-size="small"] { zoom: .93; }
html[data-text-size="large"] { zoom: 1.1; }
html[data-text-size="xlarge"] { zoom: 1.2; }
html[data-reduced-motion="true"] { --speed: 0ms; --nav-speed: 0ms; scroll-behavior: auto; }
html[data-reduced-motion="true"] *, html[data-reduced-motion="true"] *::before,
html[data-reduced-motion="true"] *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
html[data-underline-links="true"] a { text-decoration: underline !important; text-underline-offset: 3px; }
html[data-density="compact"] :is(td, th) { padding-top: 6px; padding-bottom: 6px; }
html[data-density="spacious"] :is(td, th) { padding-top: 16px; padding-bottom: 16px; }
html[data-density="compact"] .page[data-page="settings"] .setting { padding-top: 10px; padding-bottom: 10px; }
html[data-density="spacious"] .page[data-page="settings"] .setting { padding-top: 24px; padding-bottom: 24px; }

*, *::before, *::after { box-sizing: border-box; }
html {
  background: var(--paper);
  color: var(--ink);
  font: 14px/1.5 var(--font-sans);
  font-kerning: normal;
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}
body { margin: 0; background: var(--paper); min-height: 100vh; }
h1, h2, h3, h4, p, ul, ol, dl, dd, figure { margin: 0; }
h1, h2, h3, h4 { font-weight: 600; }
ul, ol { padding: 0; list-style: none; }
a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--ink-4); text-underline-offset: 3px; text-decoration-thickness: 1px; }
@media (hover: hover) { a:hover { color: var(--ink); text-decoration-color: var(--ink); } }
/* What a person copies or types is set in mono; nothing else is. */
code, kbd, pre, .mono { font-family: var(--font-mono); font-size: 12.5px; letter-spacing: 0; }
/* the face marks it: no fill behind code, and a key is its character on a hairline */
code { background: none; padding: 0; }
code.code-word { white-space: nowrap; }  /* a name inside a sentence (ui.js withCode) */
kbd { display: inline-block; min-width: 14px; padding: 0 2px; border-bottom: 1px solid var(--edge); text-align: center; line-height: 18px; color: var(--ink); }
pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
pre code { background: none; padding: 0; }
svg.icon { flex: none; display: inline-block; vertical-align: -3px; }
[hidden] { display: none !important; }
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; }
button:disabled { cursor: default; }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: var(--radius); }
::selection { background: var(--select); }
/* Figures: the text face with tabular digits, so a column of money lines up. */
.fig, .num, .tabular, td.num, th.num, .tile-value, .kv dd, time { font-variant-numeric: tabular-nums; }
.num { text-align: right; }
.muted { color: var(--ink-3); }
.secondary { color: var(--ink-2); }
/* A label: the condensed cut, quieter than what it names. */
.label { font-stretch: var(--label-stretch); font-weight: 500; color: var(--ink-3); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nowrap { white-space: nowrap; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
/* A section's title: sentence case, a step above the text it heads. */
.section-title { font-size: 15px; font-weight: 600; line-height: 1.3; color: var(--ink); }
.skip {
  position: absolute; left: 8px; top: -40px; z-index: 100; padding: 6px 10px; background: var(--paper);
  border: 1px solid var(--edge); border-radius: var(--radius); color: var(--ink);
}
.skip:focus { top: 8px; }

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

/* ---------------------------------------------------------------- the mark */

/* The Clusy mark: the logo's own artwork (clusy.io's mark, two dots joining a larger one, traced from its image into
   one filled outline in a 48-unit box), drawn in the ink, whatever the theme: black on the light theme, the light ink
   on the dark one, where a black mark would vanish. It is drawn in two places, kept the same by test_ui_dashboard.py:
   index.html's wordmark and mark.svg. */
svg.mark { display: block; flex: none; overflow: visible; }
svg.mark path { fill: currentColor; }

/* ---------------------------------------------------------------- shell */

.shell {
  display: grid;
  grid-template-columns: var(--nav-w) minmax(0, 1fr);
  grid-template-rows: var(--topbar-h) minmax(0, 1fr);
  grid-template-areas: "top top" "nav main";
  min-height: 100vh;
}
.topbar {
  grid-area: top;
  display: flex; align-items: center; gap: 10px;
  height: var(--topbar-h); padding: 0 var(--gutter) 0 18px;  /* the mark over the nav's glyphs, the name over its words */
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
  position: sticky; top: 0; z-index: 30;
}
.topbar .menu-btn { display: none; }
.wordmark {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  font-size: 15px; font-weight: 650; letter-spacing: -.01em; color: var(--ink); text-decoration: none;
}
@media (hover: hover) { .wordmark:hover { text-decoration: none; color: var(--ink); } }
/* lifted a pixel: centred on its box, the mark sat on the text's line box, low against the cap height of Portal */
.wordmark svg.mark { width: 20px; height: 20px; transform: translateY(-1px); }
/* Portal, then who makes it, smaller and quieter, on the same baseline. */
.wordmark-words { white-space: nowrap; }
.wordmark-by { margin-left: 3px; font-size: 12px; font-weight: 450; letter-spacing: 0; color: var(--ink-3); }
.topbar-actions { display: flex; align-items: center; flex: none; margin-left: 10px; }
.run-label-short { display: none; }
/* The breadcrumb continues the wordmark: Portal by Clusy Inc. / Runs / train. Shown only below a top-level page.
   The first slash stands further off than the rest, so the byline reads with the name and not as the trail's
   first place. */
.crumbs { display: flex; align-items: center; min-width: 0; flex: 1; font-size: 14px; }
.topbar-actions + .crumbs { margin-left: 6px; }
.crumbs ol { display: flex; align-items: center; gap: 9px; min-width: 0; }
.crumbs li { display: flex; align-items: center; gap: 9px; min-width: 0; color: var(--ink-2); }
/* only the page's own name gives way to a long one: the places before it keep their words whole */
.crumbs li:not(:last-child) { flex: none; }
.crumbs li::before { content: "/"; color: var(--ink-4); }
.crumbs a { color: var(--ink-2); white-space: nowrap; text-decoration: none; }
@media (hover: hover) { .crumbs a:hover { color: var(--ink); } }
.crumbs [aria-current="page"] { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar-end { display: flex; align-items: center; gap: 18px; margin-left: auto; flex: none; }
.header-control {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 32px; padding: 0 8px;
  border: 0; border-radius: var(--radius); background: none; color: var(--ink-2); font-size: 13px; font-weight: 500;
  white-space: nowrap;
}
@media (hover: hover) { .header-control:hover { color: var(--ink); background: var(--paper-2); } }
.feedback-trigger { border: 1px solid var(--rule); border-radius: var(--radius-cap); padding: 0 12px; min-height: 30px; }
.header-account { flex: none; }
.account-trigger-name { max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
.account-menu .menu-list { min-width: 232px; max-width: min(300px, calc(100vw - 24px)); }
.account-theme { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 4px 14px; font-size: 13px; }
.account-theme .theme-word { min-width: 40px; }
.feedback-form { display: grid; gap: 16px; }
.feedback-form textarea { resize: vertical; min-height: 120px; }
.feedback-sent { font-weight: 600; }
/* Freshness says something only when it has to: the data is stale, or the console is reconnecting. */
.fresh { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink-2); white-space: nowrap; }
.fresh[data-tone="warn"] { color: var(--warn); }
.fresh .glyph { fill: currentColor; stroke: currentColor; }
.fresh .glyph-ring circle { fill: none; }
.fresh:empty { display: none; }
/* the theme: its two words, a pair of buttons, the one in use pressed and in ink */
.theme-switch { display: inline-flex; align-items: center; gap: 2px; }
.theme-word {
  display: inline-flex; align-items: center; height: 26px; padding: 0 4px; border: 0; background: none;
  font-size: 13px; color: var(--ink-3); border-radius: var(--radius);
}
.theme-word[data-on] { color: var(--ink); font-weight: 600; cursor: default; }
@media (hover: hover) { .theme-word:not([data-on]):hover { color: var(--ink); } }

/* The nav: each place is its glyph (icons.js placeGlyph) and its name, on one column every row keeps (the glyphs
   under the mark, the names under the word Portal). From 900px it is open (--nav-w) or a rail of the glyphs alone
   (--rail-w, :root[data-nav="collapsed"]), which opens over the page while the pointer rests on it or the keyboard is
   in it (data-peek, app.js): the same width as open, a shadow at its edge, the page under it never moving. The names
   fade; they are still there, so each link and button keeps its name. */
.nav {
  grid-area: nav;
  display: flex; flex-direction: column;
  width: var(--nav-w); min-width: 0;
  background: var(--paper);
  border-right: 1px solid var(--rule);
  padding: 20px 8px 12px;
  position: sticky; top: var(--topbar-h); z-index: 20; height: calc(100vh - var(--topbar-h));
  overflow: hidden auto;
}
.nav-list { display: grid; gap: 2px; }
/* A place: its glyph, then its name. The one shown is in ink and heavier, and a short rule of ink marks it on the
   nav's edge, open or a rail (the rule a chosen tab wears under its word, stood upright). */
.nav-item {
  position: relative; display: grid; grid-template-columns: 20px minmax(0, 1fr); align-items: center; column-gap: 8px;
  height: 36px; padding: 0 10px; border-radius: var(--radius);
  color: var(--ink-3); font-size: 14.5px; font-weight: 500; text-decoration: none; white-space: nowrap;
}
@media (hover: hover) { .nav-item:hover { color: var(--ink); text-decoration: none; } }
.nav-item[aria-current="page"] { color: var(--ink); font-weight: 650; }
.nav-item[aria-current="page"]::before {
  content: ""; position: absolute; left: -8px; top: 9px; bottom: 9px; width: 2px; background: var(--ink);
}
.nav-item:focus-visible { outline-offset: -2px; }
.nav-glyph { display: block; flex: none; width: 20px; height: 20px; }
.nav-word, .nav-account, .theme-switch, .nav-version { transition: opacity var(--nav-speed) ease-out; }
.nav-word { min-width: 0; overflow: hidden; }  /* clipped, never an ellipsis: the rail's names are wiped in as it opens */
/* The nav's foot holds its pin control and the controller's version. Account and theme live in the top bar. */
.nav-foot {
  margin-top: auto; padding: 10px 0 0; border-top: 1px solid var(--rule);
  display: grid; gap: 2px; font-size: 13px; min-width: 0;
}
.nav-row { display: grid; grid-template-columns: 20px minmax(0, 1fr); column-gap: 8px; align-items: center; min-height: 32px; padding: 0 10px; min-width: 0; }
.nav-row > .nav-glyph { color: var(--ink-3); }
.nav-pin-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0; }
.nav-pin {
  flex: none; display: grid; grid-template-columns: 20px auto; align-items: center; column-gap: 8px; height: 32px; padding: 0 6px 0 10px;
  border: 0; border-radius: var(--radius); background: none; color: var(--ink-3); font-size: 13px; font-weight: 500;
  white-space: nowrap;
}
@media (hover: hover) { .nav-pin:hover { color: var(--ink); } }
.nav-pin:focus-visible { outline-offset: -2px; }
.nav-version { color: var(--ink-3); font-family: var(--font-mono); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; padding-right: 10px; }
.scrim { position: fixed; inset: var(--topbar-h) 0 0 0; background: var(--scrim); z-index: 25; }

@media (min-width: 900px) {
  /* the page moves once, with the nav's edge, when the nav is pinned open or collapsed */
  .shell { transition: grid-template-columns var(--nav-speed) ease-out; }
  .nav { transition: width var(--nav-speed) ease-out, box-shadow var(--nav-speed) ease-out, background-color var(--nav-speed) ease-out; }
  :root[data-nav="collapsed"] .shell:not([data-signed-out]) { grid-template-columns: var(--rail-w) minmax(0, 1fr); }
  :root[data-nav="collapsed"] .nav:not([data-peek]) { width: var(--rail-w); }
  :root[data-nav="collapsed"] .nav[data-peek] { background: var(--raised); box-shadow: var(--shadow-side); }
  /* the rail's own control is the size of a place on it, so its focus ring is the glyph's */
  :root[data-nav="collapsed"] .nav:not([data-peek]) .nav-pin { width: 40px; overflow: hidden; }
  /* on the rail the names go first and fast, and nothing hidden takes a click; the version waits for the nav open */
  :root[data-nav="collapsed"] .nav:not([data-peek]) :is(.nav-word, .nav-account, .theme-switch),
  :root[data-nav="collapsed"] .nav .nav-version {
    opacity: 0; pointer-events: none; transition-duration: 90ms;
  }
}

.content { grid-area: main; min-width: 0; padding: 32px var(--gutter) 72px; }
.content:focus { outline: none; }
.shell[data-signed-out] { grid-template-columns: minmax(0, 1fr); grid-template-areas: "top" "main"; }
.shell[data-signed-out] .nav, .shell[data-signed-out] .menu-btn, .shell[data-signed-out] #account,
.shell[data-signed-out] .crumbs { display: none; }

@media (max-width: 899px) {
  :root { --gutter: 16px; }
  .shell { grid-template-columns: minmax(0, 1fr); grid-template-areas: "top" "main"; }
  .topbar { padding: 0 12px 0 6px; gap: 6px; }
  .topbar .menu-btn { display: inline-flex; }
  /* Closed, the drawer is off screen and inert (app.js), so neither the keyboard nor a screen reader lands in it. */
  .nav {
    position: fixed; top: var(--topbar-h); left: 0; bottom: 0; width: min(var(--nav-w), 85vw); z-index: 26;
    transform: translateX(-100%); visibility: hidden; height: auto;
    transition: transform var(--speed) ease-out, visibility 0s linear var(--speed);
  }
  .shell[data-nav-open] .nav { transform: none; visibility: visible; box-shadow: var(--shadow-side); transition: transform var(--speed) ease-out; }
  /* the drawer is the nav open: its own control is the menu button, so Collapse and Keep open are not in it */
  .nav-pin { display: none; }
  .nav-item { height: auto; }
  .content { padding: 22px var(--gutter) 48px; }
  /* Every target is at least 40px: the controls, the sort buttons and group toggles of tables, switches and the
     choices of a form, and the links that stand alone (the breadcrumb, the wordmark, the drawer's foot, the skip
     link); a table's clickable row is 40px tall. */
  .btn, .icon-btn, .nav-item, .tab, .segmented button, .menu-item, .th-btn, .group-toggle, .theme-word, .account-out,
  .copy-btn, .word-toggle, .header-control { min-height: 40px; }
  .nav-row { min-height: 40px; }
  .nav-row.nav-pin-row { min-height: 24px; padding-left: 38px; }  /* the version alone, under the names */
  .icon-btn { min-width: 40px; }
  .segmented button { height: auto; }
  .table th { height: 40px; }
  .table tr.row-go td { height: 40px; }
  .check { min-height: 40px; }
  .crumbs a, .wordmark, .skip { display: inline-flex; align-items: center; min-height: 40px; }
  .shell[data-signed-out] .topbar { padding-left: var(--gutter); }  /* no menu button: the mark on the content's edge */
}
@media (max-width: 599px) {
  .topbar-end { gap: 4px; }
  .header-control { min-width: 40px; padding: 0; }
  .feedback-trigger { padding: 0 12px; }
  .header-control-label, .account-trigger-name, .header-control .account-chevron { display: none; }
  .fresh { position: absolute; top: var(--topbar-h); right: 12px; font-size: 12.5px; background: var(--paper); }
  .wordmark { font-size: 14.5px; }
  /* where the breadcrumb follows, who makes it gives way first: the word Portal stays */
  .topbar:has(.crumbs li, .topbar-actions:not([hidden])) .wordmark-by { display: none; }
  .topbar-actions { margin-left: 0; }
  .crumbs { font-size: 13.5px; }
}
@media (max-width: 419px) {
  .run-label-full { display: none; }
  .run-label-short { display: inline; }
}
@media (max-width: 359px) {
  .topbar:has(.topbar-actions:not([hidden])) .wordmark-words {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
}

/* ---------------------------------------------------------------- page scaffolding */

/* One column that can shrink: a grid item's min-width is auto, so without minmax(0, 1fr) a wide table would widen
   the page, and the page, not the table's own region, would scroll sideways. */
.page { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; align-content: start; min-width: 0; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px 20px; flex-wrap: wrap; min-height: 32px; }
/* A calm title: the page's name, not a poster. */
.page-title { font-size: 24px; font-weight: 600; letter-spacing: -.012em; line-height: 1.2; }
.page-title-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; min-width: 0; }
.page-sub { color: var(--ink-2); margin-top: 2px; max-width: 80ch; }
.page-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.toolbar { display: flex; align-items: center; gap: 10px 18px; flex-wrap: wrap; }
.toolbar .search { flex: 0 1 260px; }
.toolbar select { width: auto; min-width: 150px; max-width: 260px; }
/* A toolbar, the table it filters and the line under it read as one block. */
.list-block { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; min-width: 0; }
.row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.stack { display: grid; gap: 32px; align-content: start; }
.cols-2 { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 32px; align-items: start; }
@media (max-width: 899px) { .cols-2 { grid-template-columns: minmax(0, 1fr); } }
/* No cards: a group is a section under a rule of ink, its title on the rule's line, its content on the grid. A
   table that is its content runs flush under the title. */
.panel { border-top: 1px solid var(--ink); background: none; min-width: 0; }
.panel-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 4px 16px; flex-wrap: wrap;
  min-height: 42px; padding: 12px 0 8px;
}
.panel > .table-wrap, .panel > .panel-flush > .table-wrap { border: 0; }
.panel-title { font-size: 15px; font-weight: 600; white-space: nowrap; line-height: 1.3; }
.panel-body { padding: 4px 0 6px; }
.panel-foot { padding: 10px 0 0; border-top: 1px solid var(--rule); color: var(--ink-3); font-size: 12.5px; margin-top: 2px; }
.meta { font-size: 12.5px; color: var(--ink-3); }
.grid-panels { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 36px 32px; }
.note { font-size: 12.5px; color: var(--ink-3); }
.stack-tight { display: grid; gap: 6px; }
.text-bad { color: var(--bad); overflow-wrap: anywhere; }

/* A page's figures are a line of text, never tiles: the figure, then what it is. ledger() in ui.js writes it; the
   older tile() writes the same line. */
.ledger, .tiles { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 30px; color: var(--ink-2); font-size: 14px; line-height: 1.5; }
.ledger-item, .tile { display: inline-flex; align-items: baseline; gap: 6px; min-width: 0; }
.ledger-fig, .tile-value { font-size: 16px; font-weight: 600; color: var(--ink); white-space: nowrap; font-variant-numeric: tabular-nums; }
/* a phone's line keeps its figures: an item's long words wait for a wider window (ledger-short), an item may be one
   width's only (ledger-wide, ledger-phone) */
.ledger-phone { display: none; }
@media (max-width: 599px) {
  .ledger-wide, .ledger-short .ledger-words { display: none; }
  .ledger-phone { display: inline-flex; }
}
.tile-value { order: 1; }
.tile-label { order: 2; }
.tile-sub { order: 3; color: var(--ink-3); }
.tile-short { display: none; }
/* The reckoning: the saving worked out, once, where totals are shown. Without Portal, less what was spent, is what
   Portal saved: the minus in a column of its own before the word (an operation, never a figure's sign), the figures
   in a column, the result under a rule of ink with its share beside it, and over it what it covers. Small, never a
   tile. */
.reckoning-wrap { display: inline-grid; gap: 6px; justify-items: start; }
.reckoning-caption { font-size: 12.5px; color: var(--ink-3); font-stretch: var(--label-stretch); font-weight: 500; }
.reckoning {
  display: inline-grid; grid-template-columns: auto auto auto; column-gap: 14px; row-gap: 2px; align-items: baseline;
  font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums;
}
.reckoning dt { grid-column: 1; display: flex; align-items: baseline; }
.reckoning .reckoning-op { flex: none; width: 1.1em; color: var(--ink-2); }
.reckoning dd { grid-column: 2; text-align: right; color: var(--ink); white-space: nowrap; }
.reckoning dd.reckoning-share { grid-column: 3; text-align: left; color: var(--ink-2); }
.reckoning .reckoning-rule { grid-column: 1 / -1; height: 1px; margin: 3px 0 2px; background: var(--ink); }
.reckoning dt.reckoning-sum { color: var(--ink); font-weight: 600; }
.reckoning dd.reckoning-sum { font-weight: 600; }

/* ---------------------------------------------------------------- controls */

/* One control height: buttons, fields and segmented controls are --control-h tall (40px below 900px). A button is
   its word: no glyph rides beside it. The primary one is ink. Only a button inside a table row is smaller. */
.btn, .icon-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: var(--control-h); padding: 0 14px; border-radius: var(--radius);
  border: 1px solid var(--edge); background: var(--paper); color: var(--ink);
  font-size: 13.5px; font-weight: 550; line-height: 1; white-space: nowrap; text-decoration: none;
  transition: background var(--speed) ease-out, border-color var(--speed) ease-out, color var(--speed) ease-out;
}
.btn > svg.icon { display: none; }
@media (hover: hover) { .btn:hover:not(:disabled) { background: var(--paper-2); border-color: var(--ink-2); text-decoration: none; color: var(--ink); } }
.btn:disabled, .icon-btn:disabled { opacity: .45; }
.btn-primary { background: var(--ink); border-color: var(--ink); color: var(--paper); }
@media (hover: hover) { .btn-primary:hover:not(:disabled) { background: var(--ink-2); border-color: var(--ink-2); color: var(--paper); } }
.btn-danger { color: var(--bad); }
@media (hover: hover) { .btn-danger:hover:not(:disabled) { color: var(--bad); border-color: var(--bad); background: var(--paper); } }
.btn-ghost { border-color: transparent; background: transparent; }
@media (hover: hover) { .btn-ghost:hover:not(:disabled) { background: var(--paper-2); border-color: transparent; } }
.btn-sm { height: 26px; padding: 0 10px; font-size: 12.5px; }
.icon-btn { width: var(--control-h); padding: 0; border-color: transparent; background: transparent; color: var(--ink-2); }
@media (hover: hover) { .icon-btn:hover:not(:disabled) { background: var(--paper-2); color: var(--ink); } }
/* Copy is a word, and says Copied for a moment. */
.copy-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none; height: 26px; padding: 0 8px;
  border: 0; border-radius: var(--radius); background: none; color: var(--ink-3); font-size: 12.5px; font-weight: 550;
}
@media (hover: hover) { .copy-btn:hover { color: var(--ink); background: var(--paper-3); } }
.copy-btn[data-state="done"] { color: var(--ink); }
.copy-btn[data-state="failed"] { color: var(--bad); }
.btn-link { background: none; border: 0; padding: 0; height: auto; color: var(--ink); font-weight: 550; text-decoration: underline; text-decoration-color: var(--ink-4); text-underline-offset: 3px; }
@media (hover: hover) { .btn-link:hover { text-decoration-color: var(--ink); } }

/* A field is a line to write on: the line is --edge (3:1), ink and twice as thick while it has the keyboard. */
input[type="text"], input[type="password"], input[type="number"], input[type="search"], input[type="email"],
input[type="url"], select, .input {
  display: block; width: 100%; height: var(--control-h); padding: 0 2px;
  border: 0; border-bottom: 1px solid var(--edge); border-radius: 0; background: transparent; color: var(--ink);
  font-size: 14px; box-shadow: none;
  transition: box-shadow var(--speed) ease-out, border-color var(--speed) ease-out;
}
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }
/* a number is typed: no spinner buttons in the field */
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
select { appearance: none; -webkit-appearance: none; padding-right: 22px; cursor: pointer; }
/* A select wears the stroked chevron a sorted column does (the console's pages wrap each in .pick with icons.js's
   chevron). In a form it sits on its line as a field does; in a toolbar (.pick-words) it is its words alone, no line
   under them, the chosen one in ink. */
.pick { position: relative; display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
.pick > .icon { position: absolute; right: 2px; top: 50%; margin-top: -7px; pointer-events: none; color: var(--ink-2); }
.pick-words select { width: auto; min-width: 0; max-width: 240px; padding: 0 22px 0 0; border-bottom: 0; color: var(--ink); font-weight: 550; text-overflow: ellipsis; field-sizing: content; }
.pick-words select:focus { box-shadow: none; }
.pick-words select:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: var(--radius); }
textarea {
  display: block; width: 100%; min-height: 96px; padding: 8px 10px; resize: vertical; line-height: 1.5;
  border: 1px solid var(--edge); border-radius: var(--radius); background: transparent; color: var(--ink);
  font-family: var(--font-mono); font-size: 12.5px;
}
@media (hover: hover) { input:hover:not(:disabled):not(:focus), select:hover:not(:disabled):not(:focus), textarea:hover:not(:disabled):not(:focus) { border-color: var(--ink-2); } }
input:focus, select:focus { border-color: var(--ink); box-shadow: inset 0 -1px 0 var(--ink); outline: none; }
textarea:focus { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); outline: none; }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: none; }
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] { border-color: var(--bad); }
input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus { border-color: var(--bad); box-shadow: inset 0 -1px 0 var(--bad); }
textarea[aria-invalid="true"]:focus { border-color: var(--bad); box-shadow: inset 0 0 0 1px var(--bad); }
input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: 1; }
/* a field that keeps what is stored says so in words, not in the face of the value it would take */
input.mono.stored::placeholder { font-family: var(--font-sans); }
input:disabled, select:disabled, textarea:disabled { color: var(--ink-3); border-bottom-style: dashed; border-color: var(--rule-2); cursor: default; }
input[type="checkbox"], input[type="radio"] { width: 15px; height: 15px; margin: 0; accent-color: var(--ink); flex: none; }
/* a checkbox is a box of the outline's colour, the checked one ink with a tick of paper, in either theme; the keyboard
   that reaches it or a radio rings it in ink */
input[type="checkbox"] {
  -webkit-appearance: none; appearance: none; border: 1.5px solid var(--edge); border-radius: var(--radius); background: transparent;
  display: inline-grid; place-content: center; cursor: pointer;
}
input[type="checkbox"]::before {
  content: ""; width: 7px; height: 3.5px; margin-top: -2px; border: solid var(--paper); border-width: 0 0 2px 2px;
  transform: rotate(-45deg); visibility: hidden;
}
input[type="checkbox"]:checked { background: var(--ink); border-color: var(--ink); }
input[type="checkbox"]:checked::before { visibility: visible; }
@media (hover: hover) { input[type="checkbox"]:hover:not(:disabled):not(:checked) { border-color: var(--ink-2); } }
input[type="checkbox"]:disabled { opacity: .45; cursor: default; }
input[type="checkbox"]:focus, input[type="radio"]:focus { box-shadow: none; }
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
@media (forced-colors: active) {
  input[type="checkbox"], input[type="radio"] { forced-color-adjust: none; border-color: CanvasText; }
  input[type="checkbox"]:checked { background: CanvasText; }
  input[type="checkbox"]::before { border-color: Canvas; }
  input[type="radio"]:checked { background: radial-gradient(circle, CanvasText 0 3.4px, transparent 3.9px); }
}
/* a radio is a ring of the outline's colour, the one chosen a ring of ink with an ink disc in it, in either theme */
input[type="radio"] {
  -webkit-appearance: none; appearance: none; border: 1.5px solid var(--edge); border-radius: 50%; background: transparent;
  display: inline-grid; place-content: center; cursor: pointer;
}
input[type="radio"]:checked { border-color: var(--ink); background: radial-gradient(circle, var(--ink) 0 3.4px, transparent 3.9px); }
@media (hover: hover) { input[type="radio"]:hover:not(:disabled):not(:checked) { border-color: var(--ink-2); } }
input[type="radio"]:disabled { opacity: .45; cursor: default; }
.mono-input, input.mono { font-family: var(--font-mono); font-size: 13px; }
@media (max-width: 899px) {
  /* 16px text in a field keeps a phone's browser from zooming the page when the field takes focus */
  input[type="text"], input[type="password"], input[type="number"], input[type="search"], input[type="email"],
  input[type="url"], select, textarea, .input, .mono-input, input.mono { font-size: 16px; }
  input[type="text"], input[type="password"], input[type="number"], input[type="search"], input[type="email"],
  input[type="url"], select, .input { height: 40px; }
}
.check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
/* Search is a field like any other: no box, no magnifying glass; / reaches it from anywhere on the page. */
.search-box { position: relative; }
.search-box > svg.icon { display: none; }
/* A number with its unit after it: [ 20 ] % */
.unit-input { display: inline-flex; align-items: baseline; gap: 6px; }
.unit-input input { width: 96px; }
.unit-input .unit { color: var(--ink-3); font-size: 12.5px; white-space: nowrap; }

/* A word that is on or off where it acts at once (a log's Follow): pressed, it is in ink with the short rule the
   chosen word of a segmented choice wears; not pressed, quiet. No track, no knob. */
.word-toggle {
  position: relative; display: inline-flex; align-items: center; height: var(--control-h); padding: 0 6px; border: 0;
  border-radius: var(--radius); background: none; color: var(--ink-3); font-size: 13.5px; font-weight: 500; white-space: nowrap;
}
.word-toggle::after { content: ""; position: absolute; left: 6px; right: 6px; bottom: 1px; height: 2px; background: transparent; }
@media (hover: hover) { .word-toggle:hover { color: var(--ink); } }
.word-toggle[aria-pressed="true"] { color: var(--ink); font-weight: 600; }
.word-toggle[aria-pressed="true"]::after { background: var(--ink); }
.word-toggle:focus-visible { outline-offset: -2px; }

/* fields: a label over its line; the row layout puts the label in a column on the left and the note in a margin on
   the right (the connect sheet), folding the note under the field where the form is narrow. */
.field { display: grid; gap: 4px; }
.field > label, .field-label { font-size: 13px; font-weight: 550; color: var(--ink); }
.field-control { display: grid; gap: 4px; min-width: 0; }
.field-control > .segmented { justify-self: start; }
.field-row { display: grid; gap: 4px; min-width: 0; }
.field[data-layout="row"] { grid-template-columns: 136px minmax(0, 1fr); align-items: start; gap: 6px 18px; container-type: inline-size; }
.field[data-layout="row"] > label, .field[data-layout="row"] > .field-label { padding-top: 7px; }
.field[data-layout="row"] .field-control { grid-template-columns: minmax(0, 1fr) minmax(0, .85fr); column-gap: 18px; align-items: start; }
.field[data-layout="row"] .field-control > :not(.help) { grid-column: 1; }
.field[data-layout="row"] .field-control > .help { grid-column: 2; grid-row: 1 / span 3; padding-top: 6px; }
@container (max-width: 520px) {
  .field[data-layout="row"] .field-control { grid-template-columns: minmax(0, 1fr); }
  .field[data-layout="row"] .field-control > .help { grid-column: 1; grid-row: auto; padding-top: 0; }
}
.help { font-size: 12.5px; color: var(--ink-3); line-height: 1.5; }
.field-error { font-size: 12.5px; color: var(--bad); }
.form { display: grid; gap: 18px; }
.form-actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }

/* State: the word, with the glyph a path's end uses for it (a disc live, a ring held or on its way, a square stopped, a
   cross failed). Live states in full ink, attention in its warm tone, the rest quiet. Never a pill. */
.glyph { flex: none; display: inline-block; overflow: visible; }
.badge { display: inline-flex; align-items: center; gap: 7px; font-size: 13.5px; color: var(--ink); white-space: nowrap; line-height: 1.2; }
.badge .glyph { fill: currentColor; stroke: currentColor; }
.badge[data-tone="ok"][data-glyph="disc"] { font-weight: 600; }
.badge[data-tone="accent"] { font-weight: 550; }
.badge[data-tone="neutral"] { color: var(--ink-3); }
.badge[data-tone="warn"] { color: var(--warn); font-weight: 550; }
.badge[data-tone="bad"] { color: var(--bad); font-weight: 550; }
.badge[data-glyph="disc"] .glyph circle, .badge[data-glyph="square"] .glyph rect { stroke: none; }
.badge[data-glyph="ring"] .glyph circle, .glyph[data-tone] circle.ring { fill: none; }
.glyph[data-tone] { fill: currentColor; stroke: currentColor; }
.glyph[data-tone="ok"] { color: var(--ink); }
.glyph[data-tone="accent"] { color: var(--ink-2); }
.glyph[data-tone="warn"] { color: var(--warn); }
.glyph[data-tone="bad"] { color: var(--bad); }
.dot { display: none; }

/* A plain, quiet meter for a share of something (a cap, a budget, a share of the spend, what the run would have
   cost): a hairline track and an ink part. It is not a run's path, so it has none of the path's ends. */
.meter, .bar {
  display: inline-block; position: relative; flex: none; width: 72px; height: 4px; vertical-align: middle;
  background: var(--rule-2);
}
.meter > i, .bar > i { position: absolute; left: 0; top: 0; bottom: 0; display: block; width: var(--w, 0%); background: var(--ink-2); }
.meter[data-tone="warn"] > i, .bar[data-tone="warn"] > i { background: var(--warn); }
.meter[data-tone="bad"] > i, .bar[data-tone="bad"] > i { background: var(--bad); }
.meter[data-tone="muted"] > i, .bar[data-tone="muted"] > i { background: var(--ink-4); }
.progress { display: inline-flex; align-items: center; gap: 9px; font-variant-numeric: tabular-nums; }
.progress .mono { font-family: var(--font-sans); font-size: 13px; }

/* A notice is a sentence on the grid between two hairlines, its tone's glyph before it; no box, no fill. Over a
   group that opens on a rule of ink, that rule ends it: two rules with nothing between them would read as a row. */
.notice {
  display: flex; align-items: flex-start; gap: 10px; padding: 10px 0; position: relative;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); color: var(--ink);
}
.notice:has(+ :is(.panel, .conn-list)) { border-bottom: 0; padding-bottom: 0; }
.notice > .glyph { margin-top: 5px; }
.notice-text { flex: 1; min-width: 0; overflow-wrap: anywhere; line-height: 1.5; }
.notice-actions { display: flex; gap: 8px; flex: none; align-self: center; }
.notice .notice-actions .btn-sm { height: 26px; }

/* An empty place: one sentence where the content would be, on the grid's left edge, its action after it. A place
   still waiting for its first answer says so, and, once the controller cannot be reached, why it still waits. */
.empty { padding: 14px 0; color: var(--ink-2); }
.loading[data-loading="unreachable"] { display: flex; align-items: center; gap: 9px; color: var(--ink); }
.loading[data-loading="unreachable"] .glyph { color: var(--warn); fill: none; stroke: currentColor; }
.panel .empty { padding: 12px 0; }
.empty-action { margin-top: 10px; }

/* A list of facts: its keys in the label cut, its values in the text face. */
.kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 24px; font-size: 13.5px; }
.kv dt { color: var(--ink-3); white-space: nowrap; font-stretch: var(--label-stretch); font-weight: 500; }
.kv dd { min-width: 0; overflow-wrap: anywhere; }

/* A command to copy is a line of mono set as a shell's: a quiet "$" before it (not copied; a wrapped line hangs
   after it), the line it sits on a hairline, as a field's is, and its Copy at that line's right end, so the commands
   of a page end on one edge. No box, no bar, no fill (a fill is only the log's). Word by word, so a narrow place
   wraps between words and never inside a flag; a path breaks only before one of its slashes. A value that is not a
   command (a link, a token) has no "$" and may break anywhere. */
.cmd-box {
  display: flex; width: 100%; align-items: baseline; gap: 4px 16px; min-width: 0;
  padding: 1px 0; border-bottom: 1px solid var(--rule-2); font-family: var(--font-mono); font-size: 12.5px;
}
.cmd-box code { flex: 1 1 auto; min-width: 0; background: none; border: 0; padding: 4px 0; white-space: normal; overflow-wrap: anywhere; line-height: 1.55; }
.cmd-box code[data-prompt] { padding-left: 1.3em; text-indent: -1.3em; }
.cmd-box code[data-prompt]::before { content: "$"; display: inline-block; width: 1.3em; text-indent: 0; color: var(--ink-3); }
.cmd-word, .cmd-part { white-space: nowrap; }
.cmd-word.cmd-path { white-space: normal; }
.cmd-box .copy-btn { flex: none; align-self: baseline; height: 24px; margin-right: -4px; padding: 0 4px; font-family: var(--font-sans); }

/* tabs: words, the chosen one in ink with a short rule of ink under it */
.tabs { display: flex; gap: 24px; border-bottom: 1px solid var(--rule); overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
/* A strip wider than the screen fades at the edge it continues past (ui.js sets data-overflow). */
.tabs[data-overflow="end"] { -webkit-mask-image: linear-gradient(to right, black calc(100% - 32px), transparent); mask-image: linear-gradient(to right, black calc(100% - 32px), transparent); }
.tabs[data-overflow="start"] { -webkit-mask-image: linear-gradient(to left, black calc(100% - 32px), transparent); mask-image: linear-gradient(to left, black calc(100% - 32px), transparent); }
.tabs[data-overflow="both"] { -webkit-mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 32px), transparent); mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 32px), transparent); }
.tab {
  display: inline-flex; align-items: center; gap: 6px; padding: 0; height: 40px; margin-bottom: -1px; position: relative;
  border: 0; background: none; color: var(--ink-3); font-size: 14.5px; font-weight: 500; text-decoration: none;
  white-space: nowrap; border-radius: 0; flex: none;
}
.tab::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: transparent; }
@media (hover: hover) { .tab:hover { color: var(--ink); text-decoration: none; } }
.tab[aria-selected="true"] { color: var(--ink); font-weight: 600; }
.tab[aria-selected="true"]::after { background: var(--ink); }
.tab:focus-visible { outline-offset: -2px; }
.tab-count { font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* segmented: a choice of one, as words; the chosen one in ink with the rule under it */
.segmented {
  display: inline-flex; gap: 4px; padding: 0; border: 0; background: none; max-width: 100%; overflow-x: auto;
  height: var(--control-h); scrollbar-width: none;
}
.segmented::-webkit-scrollbar { display: none; }
.segmented[data-overflow="end"] { -webkit-mask-image: linear-gradient(to right, black calc(100% - 32px), transparent); mask-image: linear-gradient(to right, black calc(100% - 32px), transparent); }
.segmented[data-overflow="start"] { -webkit-mask-image: linear-gradient(to left, black calc(100% - 32px), transparent); mask-image: linear-gradient(to left, black calc(100% - 32px), transparent); }
.segmented[data-overflow="both"] { -webkit-mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 32px), transparent); mask-image: linear-gradient(to right, transparent, black 32px, black calc(100% - 32px), transparent); }
.segmented button {
  height: var(--control-h); padding: 0 8px; border: 0; border-radius: var(--radius); background: none; color: var(--ink-3);
  font-size: 13.5px; font-weight: 500; white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; position: relative;
}
.segmented button::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: 1px; height: 2px; background: transparent; }
@media (hover: hover) { .segmented button:hover { color: var(--ink); } }
.segmented button[aria-checked="true"] { color: var(--ink); font-weight: 600; }
.segmented button[aria-checked="true"]::after { background: var(--ink); }
.segmented button:focus-visible { outline-offset: -2px; }  /* inside the control's own scroll box */
.seg-count { font-size: 12px; font-weight: 400; color: var(--ink-3); font-variant-numeric: tabular-nums; }
@media (max-width: 899px) { .segmented { height: auto; } }

/* ---------------------------------------------------------------- tables */

/* A ledger: the head (labels in the condensed cut) over a rule of ink, the rows on hairlines, no box. Figures are
   the text face's tabular digits, right-aligned. */
.table-wrap { border: 0; overflow: auto; background: none; min-width: 0; }
.table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.table caption.caption { text-align: left; padding: 8px 0; color: var(--ink-2); caption-side: top; }
.table th {
  position: sticky; top: 0; z-index: 1;
  text-align: left; font-weight: 500; font-size: 12.5px; font-stretch: var(--label-stretch); color: var(--ink-3);
  white-space: nowrap; padding: 0 12px 6px; height: 34px; line-height: 22px; background: var(--paper);
  border-bottom: 1px solid var(--ink); vertical-align: bottom;
}
.table th:first-child, .table td:first-child { padding-left: 0; }
.table th:last-child, .table td:last-child { padding-right: 0; }
.table th.num { text-align: right; }
.th-btn {
  display: inline-flex; align-items: center; gap: 4px; height: 22px; margin: 0 -4px; padding: 0 4px;
  border: 0; background: none; color: inherit; font: inherit; font-weight: 500; border-radius: var(--radius);
}
/* The sort glyph shows on the column sorted by, and on another one only under the pointer or the keyboard. It hangs
   outside its heading's words (before a figures' heading, after a words' one), so every heading ends where its
   column's figures do. */
.th-btn { position: relative; }
.th-btn .icon { position: absolute; top: 50%; margin-top: -6px; left: calc(100% + 1px); color: var(--ink-3); opacity: 0; transition: opacity var(--speed) ease-out; }
th.num .th-btn .icon { left: auto; right: calc(100% + 1px); }
.th-btn:focus-visible .icon { opacity: 1; }
@media (hover: hover) { .th-btn:hover .icon { opacity: 1; } }
@media (hover: hover) { .th-btn:hover { color: var(--ink); } }
th[aria-sort="ascending"] .th-btn, th[aria-sort="descending"] .th-btn { color: var(--ink); font-weight: 600; }
th[aria-sort="ascending"] .icon, th[aria-sort="descending"] .icon { color: var(--ink); opacity: 1; }
.table td {
  padding: 0 12px; height: 40px; border-bottom: 1px solid var(--rule); vertical-align: middle; white-space: nowrap;
}
/* A figure in a figures' column is the text face, even where a page wrapped it in .mono. */
.table td.num, .table td.num .mono { font-family: var(--font-sans); font-size: 13.5px; }
.table tbody tr:last-child td, .table tbody tr:last-child th { border-bottom: 0; }
@media (hover: hover) { .table tbody tr:hover td { background: var(--paper-2); } }
.table tr.row-go { cursor: pointer; }
.table tr.row-go:focus-within td { background: var(--paper-2); }
.table td.wrap { white-space: normal; min-width: 240px; overflow-wrap: anywhere; }
.table td.mono { font-family: var(--font-mono); font-size: 12.5px; }
.table .row-link { color: var(--ink); font-weight: 550; text-decoration: none; }
@media (hover: hover) { .table .row-link:hover { text-decoration: underline; text-decoration-color: var(--ink-4); } }
/* The row the keyboard is on (j, k, Tab) lies between two rules of ink across its whole width, over its wash: never
   a box around its first cell, which would read as a field. */
.table tr:has(> td .row-link:focus-visible) > td { box-shadow: inset 0 2px 0 var(--ink), inset 0 -2px 0 var(--ink); }
.table tr:has(> td .row-link:focus-visible) .row-link:focus-visible { outline: none; }
.table tr.group-row th, .table tr.group-row td {
  background: var(--paper); font-weight: 600; color: var(--ink); height: 48px; position: static; font-stretch: 100%;
  font-size: 13.5px; border-bottom: 1px solid var(--edge); padding-top: 16px; vertical-align: middle; padding-bottom: 4px;
}
.table tr.group-row td { font-weight: 500; }
@media (hover: hover) { .table tr.group-row:hover td { background: var(--paper); } }
.group-toggle {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%; height: 26px; margin-left: -4px; padding: 0 4px;
  border: 0; background: none; color: inherit; font: inherit; font-weight: 600; font-size: 14.5px; border-radius: var(--radius);
}
.group-toggle .icon { color: var(--ink-3); transition: transform var(--speed) ease-out; }
.group-toggle[aria-expanded="false"] .icon { transform: rotate(-90deg); }
.group-count { font-weight: 400; color: var(--ink-3); font-size: 13px; }
.table tr.in-group > td:first-child { padding-left: 22px; }
.table-empty { padding: 16px 0; color: var(--ink-2); border-top: 1px solid var(--ink); }
/* under a section's title, whose own rule of ink is above it, an empty table's sentence needs no rule of its own */
.panel > .panel-head + .table-wrap .table-empty, .panel > .panel-head + * > .table-wrap .table-empty { border-top: 0; padding-top: 4px; }
.table-empty .empty { padding: 0; }
.table.is-empty thead, .table.is-empty tbody { display: none; }
.table-foot { font-size: 12.5px; color: var(--ink-3); }
.cell-sub { color: var(--ink-3); font-size: 12.5px; margin-top: 1px; }
.cell-stack { display: grid; line-height: 1.35; }
.table td:has(> .cell-stack) { padding-top: 6px; padding-bottom: 6px; }
.cell-fold { display: none; }
.zero { color: var(--ink-3); }
/* Row actions sit at the row's end, quiet until the row is pointed at or reached by the keyboard. */
.row-actions { display: inline-flex; gap: 6px; justify-content: flex-end; }
/* Below 600px a table keeps its identifying column and its money; the rest folds under the first cell, each fact its
   label (the label cut) and its value, kept whole, the facts apart on a line that wraps between them. The line is
   the text face whatever cell carries it; an id or a path in it keeps its mono, and a path may break before a
   slash. */
.cell-fact { display: inline-flex; align-items: baseline; gap: 5px; max-width: 100%; white-space: nowrap; }
.facts { display: inline-flex; flex-wrap: wrap; align-items: baseline; column-gap: 14px; row-gap: 0; max-width: 100%; }
.cell-fact-label { color: var(--ink-3); font-stretch: var(--label-stretch); font-weight: 500; white-space: nowrap; }
.cell-fact .mono { font-size: 12px; }
.cell-fact:has(.cell-path) { white-space: normal; }
.cell-path { overflow-wrap: anywhere; }
@media (max-width: 599px) {
  .table td.col-fold, .table th.col-fold { display: none; }
  .cell-fold {
    display: flex; flex-wrap: wrap; column-gap: 14px; row-gap: 1px; margin-top: 2px;
    font: 400 12.5px/1.45 var(--font-sans); color: var(--ink-2); white-space: normal;
  }
  .table td:has(.cell-fold) { padding-top: 7px; padding-bottom: 7px; height: auto; }
}

/* ---------------------------------------------------------------- the path (ui.js) */

/* A run's line: its machines' segments in their providers' colours with round ends, an interchange (a station: a
   ring on the line) where it moved, the head's glyph at how far it got, a dotted track for what is left. The same
   strokes at every size. A run that ended is drawn thin and quiet; the one that runs now is the strongest line. */
.path { display: block; overflow: visible; flex: none; }
.path-seg { stroke-width: 3; stroke-linecap: round; fill: none; }
.path-seg-unknown { stroke: var(--ink-4); }
/* E2B is ink, and its line is dashed so it never reads as This machine's */
.path-seg-e2b, .chart-track-e2b { stroke-dasharray: 5 3; stroke-linecap: butt; }
.path-station { fill: var(--paper); stroke: var(--ink); stroke-width: 1.5; }
.path-track { stroke: var(--ink-4); stroke-width: 1.5; stroke-dasharray: 0 4; stroke-linecap: round; fill: none; }
.path-disc { stroke: var(--paper); stroke-width: 2; paint-order: stroke; }
.path-ring { fill: var(--paper); stroke: var(--ink); stroke-width: 1.75; }
.path-head-park .path-ring, .path-head-unseen .path-ring { stroke: var(--warn); }
.path-head-wait .path-ring { stroke: var(--ink-3); }
.path-square { fill: var(--ink-2); }
.path-cross { stroke: var(--bad); stroke-width: 2; stroke-linecap: round; fill: none; }
.path[data-quiet] .path-seg { stroke-width: 1.75; }
.path[data-quiet] .path-station { stroke: var(--ink-3); stroke-width: 1.25; }
.path[data-quiet] .path-track { stroke-width: 1.25; }
/* a row whose machines are not known yet: the dotted track alone, never a line that would pass for a finished run's */
.path-pending { stroke-dasharray: 0 4; }
.path-row { display: inline-flex; align-items: center; min-height: 14px; vertical-align: middle; }
/* in a row under the pointer, the stations and rings are filled with the row's own colour (a disc keeps its own) */
tr.row-go:focus-within .path-station, tr.row-go:focus-within .path-ring { fill: var(--paper-2); }
@media (hover: hover) { tr:hover .path-station, tr:hover .path-ring { fill: var(--paper-2); } }
tr.row-go:focus-within .path-disc { stroke: var(--paper-2); }
@media (hover: hover) { tr:hover .path-disc { stroke: var(--paper-2); } }

/* The path under a run's title: each machine named under its segment with its price and its time there (the split
   of the line is time), how far the run got over its head (the length of the line is the share done). */
.path-view { position: relative; min-width: 0; padding-bottom: 24px; }
.path-full .path-seg { stroke-width: 4; }
.path-full .path-station { stroke-width: 2; }
.path-full .path-track { stroke-width: 2; stroke-dasharray: 0 5; }
.path-names { position: absolute; left: 0; right: 0; top: 24px; height: 18px; }
.path-name, .path-at {
  position: absolute; top: 0; font-size: 12.5px; line-height: 18px; color: var(--ink-2); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; font-stretch: var(--label-stretch); font-weight: 550;
}
.path-name-sub { font-weight: 400; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.path-name-sub > span { margin-left: 8px; }  /* a machine's price and time after its name, apart by space, no dot */
/* over the line: how far the run got, over its head, and the name of a machine too short a stretch to carry it */
.path-over { top: -44px; overflow: visible; }
.path-at { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; font-stretch: 100%; }
.path-leader { stroke: var(--ink-4); stroke-width: 1; fill: none; }

/* the map: a lane per provider, interchanges between them, the time axis under them */
.path-map { position: relative; min-width: 0; }
.path-mapsvg { position: absolute; left: 0; top: 0; }
.map-labels { position: absolute; inset: 0; pointer-events: none; }
.map-labels > * { pointer-events: auto; }
.map-lane { stroke: var(--rule); stroke-width: 1; }
.map-seg { stroke-width: 6; }
.map-link { stroke: var(--ink); stroke-width: 2; stroke-linecap: round; }
.map-station { fill: var(--paper); stroke: var(--ink); stroke-width: 2; }
.map-station-small { stroke-width: 1.75; }
.map-spur { stroke: var(--bad); stroke-width: 1.75; stroke-dasharray: 3 3; }
.map-tick { stroke: var(--ink-4); stroke-width: 1; }
.map-move-rule { stroke: var(--ink-4); stroke-width: 1; stroke-dasharray: 1 3; }
.map-lane-name { position: absolute; font-size: 13px; font-weight: 600; color: var(--ink); line-height: 16px; white-space: nowrap; }
.map-seg-name {
  position: absolute; font-size: 12.5px; line-height: 16px; color: var(--ink-2); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; font-stretch: var(--label-stretch); font-weight: 550;
}
.map-time, .map-move, .map-head { position: absolute; font-size: 12px; line-height: 16px; color: var(--ink-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.map-move { color: var(--ink-2); font-stretch: var(--label-stretch); font-weight: 550; }
.map-move-off { color: var(--bad); }
.map-head { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }

/* A provider's line, the key a name wears where its colour matters (a list of connections, a table of spend):
   solid once it is connected, dotted while it is only available. */
.pline { display: inline-block; flex: none; width: 18px; height: 3px; border-radius: var(--radius-cap); vertical-align: middle; background: var(--c, var(--ink-3)); }
.pline[data-off] { height: 3px; background: radial-gradient(circle, var(--c, var(--ink-3)) 1.1px, transparent 1.4px) 0 50% / 4px 3px repeat-x; }
.pline[data-key="e2b"]:not([data-off]) { border-radius: 0; background: repeating-linear-gradient(to right, var(--c) 0 5px, transparent 5px 8px); }
/* The key to the paths, as a transit map has one: each provider's line by its name, then the interchange of a move. */
.path-key { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 18px; font-size: 12.5px; color: var(--ink-3); }
.path-key-item { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.path-key-station { display: inline-block; width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--ink); background: var(--paper); }

/* ---------------------------------------------------------------- menus */

.menu { position: relative; display: inline-flex; }
.menu-list {
  position: absolute; top: calc(100% + 4px); z-index: 40; min-width: 184px; max-width: 320px; padding: 5px 0;
  background: var(--raised); border: 1px solid var(--rule-2); border-radius: var(--radius-float); box-shadow: var(--shadow);
}
.menu-list[data-align="end"] { right: 0; }
.menu-list[data-align="start"] { left: 0; }
.menu-list[data-side="top"] { top: auto; bottom: calc(100% + 6px); }
.menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 0 14px; height: 32px;
  border: 0; border-radius: 0; background: none; color: var(--ink); text-align: left; font-size: 13.5px;
  white-space: nowrap;
}
.menu-item > svg.icon { display: none; }
.menu-item:focus-visible { background: var(--paper-3); outline: none; }
@media (hover: hover) { .menu-item:hover:not(:disabled) { background: var(--paper-3); outline: none; } }
.menu-item:disabled { color: var(--ink-3); }
.menu-item[data-danger] { color: var(--bad); }
.menu-sep { height: 1px; margin: 5px 0; background: var(--rule); }
.menu-head { display: grid; gap: 2px; padding: 7px 14px 9px; min-width: 0; }
.menu-head-title { font-weight: 600; }
.menu-head-sub { font-size: 12.5px; color: var(--ink-3); overflow-wrap: anywhere; }
.menu-error { padding: 4px 14px 6px; white-space: normal; max-width: 280px; }

/* ---------------------------------------------------------------- dialogs and sheets */

dialog { color: var(--ink); }
dialog::backdrop { background: var(--scrim); }
.dialog {
  width: min(500px, calc(100vw - 32px)); max-height: calc(100vh - 48px); padding: 0; margin: auto;
  border: 1px solid var(--rule-2); border-radius: var(--radius-float); background: var(--raised); box-shadow: var(--shadow);
  overflow: auto;
}
.dialog[data-size="lg"] { width: min(760px, calc(100vw - 32px)); }
.dialog[data-size="sm"] { width: min(400px, calc(100vw - 32px)); }
.dialog-head, .sheet-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 20px 20px 14px;
}
.dialog-heading, .sheet-heading { display: grid; gap: 4px; min-width: 0; }
.dialog-title, .sheet-title { font-size: 19px; font-weight: 600; line-height: 1.25; letter-spacing: -.008em; }
.sheet-title { display: flex; align-items: center; gap: 10px; }
.dialog-title:focus, .sheet-title:focus { outline: none; }
.dialog-desc, .sheet-desc { color: var(--ink-2); }
.dialog-body, .sheet-body { padding: 0 20px 20px; display: grid; gap: 16px; }
.dialog-error, .sheet-error { padding: 0 20px 12px; }
.dialog-foot, .sheet-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 14px 20px;
  border-top: 1px solid var(--rule);
}
.dialog-actions, .sheet-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-wrap: wrap; justify-content: flex-end; }
/* Inside what floats, the fields and tables sit on the raised surface. */
.dialog .table-wrap, .sheet .table-wrap { background: transparent; }
.dialog .table th, .sheet .table th { background: var(--raised); }

.sheet {
  position: fixed; inset: 0 0 0 auto; width: 600px; max-width: 100vw; height: 100%; max-height: 100%; margin: 0;
  padding: 0; border: 0; border-left: 1px solid var(--rule-2); border-radius: 0; background: var(--raised);
  box-shadow: var(--shadow); display: flex; flex-direction: column;
}
.sheet[open] { display: flex; }
.sheet-head { padding: 22px 24px 16px; border-bottom: 1px solid var(--rule); flex: none; }
.sheet-body { padding: 22px 24px; flex: 1; overflow: auto; align-content: start; gap: 28px; }
.sheet-foot { flex: none; background: var(--raised); padding: 14px 24px; }
.sheet-section { display: grid; gap: 12px; min-width: 0; }
.sheet-section > .section-title + * { margin-top: -4px; }
/* What a sheet shows about what is stored: the same label column as its form's rows. */
.sheet .kv { grid-template-columns: 136px minmax(0, 1fr); gap: 9px 18px; }
.sheet .kv dt { white-space: normal; }
@media (max-width: 699px) {
  .sheet { width: 100vw; border-left: 0; }
  .sheet-head { padding: 18px 16px 14px; }
  .sheet-body { padding: 18px 16px; }
  .sheet-foot { padding: 12px 16px; }
  .field[data-layout="row"] { grid-template-columns: minmax(0, 1fr); }
  .field[data-layout="row"] > label, .field[data-layout="row"] > .field-label { padding-top: 0; }
  .sheet .kv { grid-template-columns: max-content minmax(0, 1fr); }
}

/* the move dialog: the offers to choose from, the run's machine now above them. A row is chosen by a click anywhere
   on it; only the one chosen wears the fill, so the pointer over another never passes for the choice. */
.plan-now { color: var(--ink-2); }
.plan-summary strong, .plan-now strong { font-weight: 600; color: var(--ink); }
.plan-offers td { height: 42px; }
.plan-offers .table tbody tr { cursor: pointer; }
@media (hover: hover) { .plan-offers .table tbody tr:hover td { background: none; } }
@media (hover: hover) { .plan-offers .table tbody tr:hover input[type="radio"]:not(:checked) { border-color: var(--ink-2); } }
.plan-offers .table tbody tr[data-chosen] td { background: var(--paper-2); }
@media (hover: hover) { .plan-offers .table tbody tr[data-chosen]:hover td { background: var(--paper-2); } }
.plan-pick { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; }
.plan-machine { display: grid; line-height: 1.3; }
.plan-sub { display: none; }
.plan-spec { display: grid; gap: 6px; padding: 4px 0 0; }
.plan-spec .field-control { padding-left: 24px; }
.plan-spec input[type="text"] { max-width: 420px; }
@media (max-width: 699px) {
  /* the offers fold GPU, region, market and boot under the machine, so the price stays in view */
  .plan-offers .col-fold { display: none; }
  .plan-sub { display: block; font-size: 12.5px; color: var(--ink-3); white-space: normal; }
  .plan-offers td { height: auto; padding-top: 7px; padding-bottom: 7px; }
}

/* The steps to set something up (ui.js steps): one list wherever the console gives them, under a rule (of ink on a
   page, a hairline inside a section, none in a dialog), each step's number hanging in a gutter on its title's line,
   its sentence and its commands beside, the steps apart on hairlines. */
.steps { display: grid; grid-template-columns: minmax(0, 1fr); border-top: 1px solid var(--ink); }
.panel .steps { border-top-color: var(--rule); }
.dialog .steps { border-top: 0; }
.sheet .steps { border-top-color: var(--rule); }
.step { display: grid; grid-template-columns: 36px minmax(0, 1fr); row-gap: 10px; padding: 18px 0 20px; min-width: 0; }
.step + .step { border-top: 1px solid var(--rule); }
.dialog .step:first-child { padding-top: 2px; }
.dialog .step:last-child { padding-bottom: 4px; }
.step-n { grid-column: 1; grid-row: 1; font-size: 15px; font-weight: 600; line-height: 21px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.step-title { grid-column: 2; grid-row: 1; font-size: 15px; font-weight: 600; line-height: 21px; color: var(--ink); }
.step-body { grid-column: 2; display: grid; gap: 10px; min-width: 0; }
.step-body > p { color: var(--ink-2); max-width: 80ch; margin-top: -4px; }
.step-body > .cmd-box + .cmd-box { margin-top: -6px; }
.step-body q { color: var(--ink); }
@media (max-width: 599px) {
  .step { grid-template-columns: 26px minmax(0, 1fr); }
}
/* where a connection's values come from, in its sheet: notes as the margin's are (one size, one ink), the command
   among them */
.how { display: grid; gap: 10px; padding: 0 0 2px; }
.danger-zone { padding-top: 18px; border-top: 1px solid var(--rule); }

/* a choice of one among a few, each with a line under it (the token dialog's scopes) */
.choices { border: 0; margin: 0; padding: 0; display: grid; gap: 10px; min-width: 0; }
.choices legend { padding: 0; margin-bottom: 4px; font-size: 13px; font-weight: 550; }
.choice { align-items: flex-start; line-height: 1.4; }
.choice input { margin-top: 3px; }
.choice-sub { display: block; font-size: 12.5px; color: var(--ink-3); text-wrap: pretty; }
.plain-list { display: grid; gap: 2px; font-family: var(--font-mono); font-size: 12.5px; }

/* The keys there are (?): a short list, each key in its cap. */
.keys-list { grid-template-columns: max-content minmax(0, 1fr); gap: 8px 18px; align-items: baseline; }
.keys-list dt { white-space: nowrap; font-stretch: 100%; }
.keys-list dt kbd + kbd { margin-left: 6px; }
.keys-list dd { color: var(--ink-2); }

/* toasts: bottom right, out of the way of the pointer; on a phone at the top, clear of the thumb's controls */
.toasts {
  position: fixed; right: 20px; bottom: 20px; z-index: 60; display: grid; gap: 8px;
  max-width: min(360px, calc(100vw - 32px)); pointer-events: none;
}
.toast {
  padding: 9px 14px; border-radius: var(--radius-float); background: var(--ink); color: var(--paper); font-size: 13.5px;
  box-shadow: var(--shadow);
}
@media (max-width: 599px) {
  .toasts { top: calc(var(--topbar-h) + 8px); bottom: auto; left: 16px; right: 16px; max-width: none; }
}

/* ---------------------------------------------------------------- logs and code */

.log {
  font-family: var(--font-mono); font-size: 12.5px; line-height: 1.6; background: var(--paper-2);
  border-radius: var(--radius); overflow: auto; max-height: 70vh; padding: 10px 0;
}
.log-line { display: flex; gap: 14px; padding: 0 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
.log-line .ln { flex: none; width: 5ch; text-align: right; color: var(--ink-3); user-select: none; }

/* ---------------------------------------------------------------- sign-in, and a card to approve */

/* A card that asks a person for one thing on a page of its own (to sign in; to approve or deny a terminal's
   sign-in; to accept an invitation): the only card the language has, because it is the only thing on its page. It is
   centred on the width at a fixed top, so its title holds still whatever the card shows (an outcome taking the
   choice's place, a page of another state). In its order: its title; why the page is here when that is news; what it
   is about when that is a thing to read, set large (a terminal's code); its sentence; its facts, on a hairline; its
   field or choices; its actions at its foot on the right, each the width of its word; another way in, a quiet line on
   a hairline under them. On a phone the card is the page: no box, the same order on the content's edge, its actions
   sharing the width. */
.approve { display: grid; justify-items: center; align-content: start; padding: clamp(32px, 10vh, 96px) 0 6vh; }
.approve-card {
  width: min(440px, 100%); display: grid; gap: 18px; padding: 28px 28px 24px; border: 1px solid var(--rule-2);
  border-radius: var(--radius-float); background: var(--raised);
}
.approve-title { font-size: 19px; font-weight: 600; line-height: 1.25; }
.approve-title:focus { outline: none; }  /* given the keyboard when the card's content is replaced, as a dialog's is */
.approve-lead { display: grid; gap: 6px; min-width: 0; }
/* the sentences only: a field's help and error, and a notice, keep their colours */
.approve-card > p, .approve-lead > p { color: var(--ink-2); }
.approve-card > .kv {
  grid-template-columns: max-content minmax(0, 1fr); column-gap: 28px; padding-top: 16px; border-top: 1px solid var(--rule);
}
.approve-actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; padding-top: 4px; }
.approve-aside { padding-top: 16px; border-top: 1px solid var(--rule); font-size: 13px; line-height: 1.5; }
/* after a notice, whose own hairline closes it, the other way in needs none of its own; a notice the card ends on
   has no hairline under it, over the card's own edge; one right under the title opens on none */
.approve-card > .notice + .approve-aside { padding-top: 0; border-top: 0; }
.approve-card > .notice:last-child { border-bottom: 0; padding-bottom: 0; }
.approve-card > .approve-title + .notice { margin-top: -4px; }
/* a short command named in a sentence stays on one line */
.approve-card p code { white-space: nowrap; }
@media (max-width: 599px) {
  .approve { justify-items: stretch; padding: 2px 0 0; }
  .approve-card { width: 100%; padding: 0; border: 0; background: none; gap: 20px; }
  .approve-title { font-size: 22px; letter-spacing: -.008em; }
  .approve-card > .kv { column-gap: 20px; }
  .approve-actions .btn { flex: 1 1 0; }
}

/* ---------------------------------------------------------------- misc */

.not-found { justify-items: start; }
.not-found p { color: var(--ink-2); }
.load-error { max-width: 640px; }
