/* The dashboard's whole appearance. Served at /app/assets/dashboard.css.
 *
 * Every rule here is keyed to a class that `web::ui::parts` or `web::ui::shell` emits:
 * a page chooses components, never colours, sizes or layout. Tokens come from
 * /brand/theme.css (--bg --bg2 --bg3 --fg --fg2 --fg3 --line --line2 --accent
 * --accent-ink --ok --warn --err --tag-l --sans --mono --elevation-* --scrim) and are
 * never redefined here.
 *
 * The signed-in pages share one header, one content column, and a narrow Settings list.
 */
/* ── base ───────────────────────────────────────────────────────────────── */
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 14px/1.45 var(--sans);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
p {
  margin: 0;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

table {
  border-collapse: collapse;
  width: 100%;
}

img {
  max-width: 100%;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The first thing a keyboard reaches, and the only thing it shows. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 5;
  background: var(--bg2);
  border: 1px solid var(--line2);
  border-radius: 8px;
}

.skip:focus {
  left: 8px;
  top: 8px;
  padding: 8px 12px;
}

/* Announcements from dashboard.js: heard, never seen. */
.live,
.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── the mark ─────────────────────────────────────────────────────────────── */

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--fg);
}

.brand:hover {
  text-decoration: none;
}

.brand img {
  display: block;
  flex: none;
}

.brand .word {
  font-weight: 700;
  font-size: 16px;
  line-height: 28px;
  letter-spacing: -0.01em;
}

/* ── page header ────────────────────────────────────────────────────────── */
.crumbs {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--fg3);
  height: 20px;
}

.crumbs a {
  color: var(--fg3);
}

.crumbs a:hover {
  color: var(--fg);
  text-decoration: none;
}

.head .title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 32px;
}

.crumbs + .title {
  margin-top: 8px;
}

h1 {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 32px;
}

.title .name {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

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

.actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

/* An id or a count written beside the page's actions reads as a note, not a value. */
.actions .mono {
  font-size: 12px;
  color: var(--fg3);
}

/* ── rhythm ─────────────────────────────────────────────────────────────── */
/* The only vertical spacing on a page: a wrapper takes the margin and its contents sit
   flush inside. */
main .head + * {
  margin-top: 24px;
}

main .head + .sec {
  margin-top: 40px;
}

main .sec + * {
  margin-top: 40px;
}

main .panel + *,
main .tbl + * {
  margin-top: 40px;
}

.sec {
  margin-top: 40px;
}

.sec h2 {
  margin: 0 0 12px;
  font-size: 15px;
  font-weight: 600;
}

.sec .banner + * {
  margin-top: 12px;
}

/* ── panel and settings rows ────────────────────────────────────────────── */
.panel,
.tbl {
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
}

/* A panel's rows run edge to edge between their dividers, so each row carries its own
   inset. Anything else a panel holds is content — a request's view, the form it offers,
   a summary, an editor — and content needs that same inset rather than the border. */
.panel > :not(.row, .disclosure) {
  padding: 18px 20px;
}

.row {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  align-items: center;
  gap: 16px;
  padding: 0 20px;
  height: 52px;
}

.row:has(.act) {
  grid-template-columns: 160px minmax(0, 1fr) auto;
}

.panel .row + .row,
.panel .row + .disclosure {
  border-top: 1px solid var(--line);
}

.row .label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--fg2);
}

.row .value {
  min-width: 0;
}

.row .act {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.row.open {
  height: auto;
  min-height: 52px;
  padding: 12px 20px;
}

/* The second line: the few facts that belong beside a name. One line, whatever the
   width — ellipsised rather than wrapped, and never bolded by the column it sits in. */
.facts {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 400;
  color: var(--fg3);
}

/* ── table ──────────────────────────────────────────────────────────────── */
.tbl {
  overflow-x: auto;
}

.tbl th {
  text-align: left;
  font-weight: 500;
  font-size: 12px;
  color: var(--fg3);
  padding: 0 16px;
  height: 36px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.tbl th.center {
  text-align: center;
  padding: 0 12px;
}

.tbl td {
  padding: 0 16px;
  height: 44px;
  border-bottom: 1px solid var(--line);
}

.tbl tbody tr:last-child td {
  border-bottom: 0;
}

.tbl tbody tr:hover {
  background: color-mix(in srgb, var(--bg3) 45%, transparent);
}

.tbl td.center {
  text-align: center;
  padding: 0 12px;
}

/* The first column names the row, and the design sets that name in 600 — unless the
   column is itself a machine value (an audit's event kind), which stays plain. */
.tbl td:first-child:not(.mono) {
  font-weight: 600;
}

/* ── badge ──────────────────────────────────────────────────────────────── */
/* A state chip alone (VIEW.md §8): the chip face, its label in its tone on its tone at
   `--chip-tint`; a neutral one keeps the face's own fill and ink. */
.badge {
  display: inline-flex;
  align-items: center;
  height: var(--chip-height);
  padding: 0 var(--chip-pad);
  border-radius: var(--chip-radius);
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}

.badge.ok {
  background: color-mix(in srgb, var(--ok) var(--chip-tint), transparent);
  color: var(--ok);
}

.badge.warn {
  background: color-mix(in srgb, var(--warn) var(--chip-tint), transparent);
  color: var(--warn);
}

.badge.err {
  background: color-mix(in srgb, var(--err) var(--chip-tint), transparent);
  color: var(--err);
}

.badge.mute {
  background: color-mix(in srgb, var(--fg) var(--chip-wash), transparent);
  color: var(--fg2);
}

/* ── buttons ────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  padding: 0 12px;
  border-radius: 8px;
  border: 1px solid var(--line2);
  background: var(--bg2);
  color: var(--fg);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

/* Author `display` would otherwise keep a hidden button in the layout: the copy
   control on a published page is a `.btn` that stays hidden until the script proves
   a clipboard exists. */
.btn[hidden] {
  display: none;
}

.btn:not(:disabled):hover {
  border-color: var(--fg3);
  text-decoration: none;
}

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

.btn.primary:not(:disabled):hover {
  border-color: var(--accent);
  filter: brightness(1.06);
}

.btn.ghost {
  border: 0;
  background: transparent;
  color: var(--fg2);
}

.btn.ghost:not(:disabled):hover {
  background: var(--bg3);
  color: var(--fg);
}

.btn.danger-outline {
  color: var(--err);
}

.btn.danger-outline:not(:disabled):hover {
  border-color: var(--err);
}

.btn.danger-fill {
  border-color: var(--err);
  background: var(--err);
  color: var(--err-ink);
}

.btn.danger-fill:not(:disabled):hover {
  border-color: var(--err);
  filter: brightness(1.06);
}

.btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* An action scoped to a row is smaller than the page's own actions. */
.row .act .btn {
  height: 28px;
  padding: 0 10px;
}

/* The one square control: the way out of a dialog. */
.dlg-close button {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: 0;
  background: transparent;
  color: var(--fg2);
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  flex: none;
}

.dlg-close button:not(:disabled):hover {
  background: var(--bg3);
  color: var(--fg);
  text-decoration: none;
}

/* ── fields ─────────────────────────────────────────────────────────────── */
.input {
  width: 200px;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--line2);
  border-radius: 8px;
  background: var(--bg2);
  color: var(--fg);
  font-family: inherit;
  font-size: 14px;
  outline: none;
}

.input::placeholder {
  color: var(--fg3);
}

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

.input.mono {
  font-family: var(--mono);
  font-size: 12.5px;
}

.inline {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* A key pasted into a form is long; a date or a name is not. */
.disclosure-body .inline .input.mono {
  flex: 1;
  min-width: 0;
  width: auto;
}

.row.open .inline .input {
  width: 240px;
}

.row.open .inline .input.mono {
  width: 200px;
}

.tbl td .input.mono {
  width: 320px;
  height: 32px;
  border-color: var(--accent);
}

/* The pairing-code and second-factor pages write their own labelled field. */
.field {
  display: grid;
  gap: 6px;
}

.field .lbl {
  font-size: 13px;
  font-weight: 500;
  color: var(--fg2);
}

/* One-time codes: short, monospace, never stretched across the page. */
input.code {
  width: 12ch;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--line2);
  border-radius: 8px;
  background: var(--bg2);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.08em;
  outline: none;
}

input.code:focus {
  border-color: var(--accent);
}

/* ── disclosure ─────────────────────────────────────────────────────────── */
.disclosure > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 20px;
  height: 40px;
  border-top: 1px solid var(--line);
  color: var(--fg2);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

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

.disclosure > summary:hover {
  color: var(--fg);
}

.disclosure > summary svg {
  flex: none;
  transition: transform var(--motion-quick) var(--ease-standard);
}

/* What can be said about the hidden thing without opening it. */
.disclosure .meta {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  color: var(--fg3);
}

.disclosure[open] > summary svg {
  transform: rotate(90deg);
}

.disclosure-body {
  padding: 12px 20px 16px;
}

/* A disclosure that is all a panel holds is a row of that panel. */
.panel > .disclosure:only-child > summary {
  height: 52px;
  border-top: 0;
  color: var(--fg);
  font-size: 14px;
  font-weight: 400;
}

.panel > .disclosure:only-child > summary:hover {
  background: color-mix(in srgb, var(--bg3) 45%, transparent);
}

/* ── connect ────────────────────────────────────────────────────────────── */
/* The homepage's connect card, as is: two lines behind a tab strip, one copy. */
.connect {
  background: var(--bg2);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  min-width: 0;
}

.connect-tabs {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px 6px 0;
}

.connect .tab {
  height: 26px;
  line-height: 26px;
  padding: 0 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--fg2);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}

.connect .tab[aria-selected="true"] {
  background: var(--bg3);
  color: var(--fg);
}

.connect-line {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 6px 0 14px;
}

/* Both lines share one grid cell, so the card is as wide as the longer of them and
   never resizes when the tab changes. */
.connect-lines {
  display: grid;
  flex: 1;
  min-width: 0;
}

.connect-lines code {
  grid-area: 1 / 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 13.5px;
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  user-select: all;
}

.connect-lines code[data-off] {
  visibility: hidden;
}

/* ── copy ───────────────────────────────────────────────────────────────── */
/* A value and the control that copies it. The box is 40px when the value is one
   line and grows by whole lines when it wraps — never scrolls, never clips. The icon
   sits at the top right: on a one-line box that is also its centre (3px + 32px + 3px
   = 38px inside a 1px border), and on a taller one it stays with the first line.
   12px of text inset on the left, 3px on the right so the icon's own border sits 3px
   in from the box's. */
div.copy {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-height: 40px;
  padding: 3px 3px 3px 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

div.copy code {
  flex: 1;
  min-width: 0;
  padding: 6px 0;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 20px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: all;
}

/* ── the copy control ───────────────────────────────────────────────────── */
/* One control everywhere a value can be copied: the connect card, a copy box, the
   trailing action of a row. 32px square, icon centred, a tick while the copy is
   fresh. */
.copy-btn {
  display: grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line2);
  border-radius: 8px;
  background: var(--bg2);
  color: var(--fg2);
  cursor: pointer;
}

.copy-btn:not(:disabled):hover {
  border-color: var(--fg3);
  color: var(--fg);
}

.copy-btn[hidden] {
  display: none;
}

.copy-btn svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
}

.copy-btn .copy-done {
  display: none;
  stroke-width: 1.8;
  stroke-linecap: round;
}

.copy-btn.is-copied {
  color: var(--ok);
}

.copy-btn.is-copied .copy-idle {
  display: none;
}

.copy-btn.is-copied .copy-done {
  display: block;
}

/* ── empty states ───────────────────────────────────────────────────────── */
.empty {
  min-height: 176px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  color: var(--fg3);
  font-size: 13px;
}

/* An empty list inside a panel is one quiet line, not a page's worth of air. */
.panel > .empty {
  min-height: 0;
  padding: var(--space-5) var(--space-4);
  text-align: center;
}

/* ── banner ─────────────────────────────────────────────────────────────── */
.banner {
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 13px;
}

.banner.ok {
  background: color-mix(in srgb, var(--ok) 12%, transparent);
  color: var(--ok);
}

.banner.warn {
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  color: var(--warn);
}

.banner.err {
  background: color-mix(in srgb, var(--err) 12%, transparent);
  color: var(--err);
}

.banner.mute {
  background: var(--bg3);
  color: var(--fg2);
}

/* ── dialog ─────────────────────────────────────────────────────────────── */
/* Every question the dashboard asks and every field it takes, over the page rather
   than inside it. The browser lifts it into the top layer when it is opened, so where
   the markup sits — a table cell, a list item — decides nothing about how it reads. */
.dlg {
  width: calc(100vw - 32px);
  max-width: 440px;
  padding: 24px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg2);
  color: var(--fg);
}

/* Written for the open state alone: closed, the browser's own `display: none` stands.
   The centring lives here too, and so outranks the flow-spacing rules a page's stack of
   sections writes onto whatever follows a panel or a live region. */
.dlg[open] {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 12px;
  margin: auto;
}

.dlg::backdrop {
  background: var(--scrim);
}

.dlg h2 {
  grid-column: 1;
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

/* The way out that changes nothing, in the corner every window keeps it in. */
.dlg-close {
  grid-column: 2;
  margin: 0;
}

.dlg-body {
  grid-column: 1 / -1;
  margin-top: 16px;
  display: grid;
  gap: 16px;
  font-size: 13px;
  color: var(--fg2);
}

.dlg-body p {
  margin: 0;
}

/* The decisions read from the right, the deed last. */
.dlg .act {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.dlg .act form {
  display: contents;
}

/* A form here has the whole width: there is no column beside it to line up with. */
.dlg .inline {
  width: 100%;
  gap: 12px;
}

.dlg .inline .input {
  flex: 1;
  min-width: 0;
  width: auto;
}

/* A swatch is read, not filled in: it takes the room a swatch needs and no more. */
.dlg .inline .input[type="color"] {
  flex: none;
  width: 44px;
  padding: 2px;
}

/* ── values ─────────────────────────────────────────────────────────────── */
.mono {
  font-family: var(--mono);
  font-size: 12.5px;
}

/* The second factor: a QR to scan and the codes that outlive the phone. */
.qr {
  width: max-content;
  padding: 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.qr svg {
  display: block;
  width: 160px;
  height: auto;
}

/* ── documents without the app nav ──────────────────────────────────────── */
body.bare main {
  margin: 0 auto;
  max-width: 420px;
  padding: 64px 24px;
  display: grid;
  gap: 16px;
  justify-items: start;
}

body.bare .brand {
  margin-bottom: 8px;
}

body.bare p {
  color: var(--fg2);
}

/* A consent is read by comparing two strings, so those two are the page's largest type
   and the sentences under them are the small print they always were. */
.facts {
  align-self: stretch;
  display: grid;
  gap: 2px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg2);
}

.fact-code {
  font-family: var(--mono);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--fg);
}

.fact-who {
  font-size: 15px;
  font-weight: 700;
  color: var(--fg);
  overflow-wrap: anywhere;
}

.fact-note {
  font-size: 13px;
  color: var(--fg2);
  overflow-wrap: anywhere;
}

body.bare .panel,
body.bare .copy,
body.bare .banner {
  align-self: stretch;
}

body.bare form {
  display: grid;
  gap: 12px;
  justify-items: start;
}

body.bare .actions,
body.bare form .btn + .btn {
  display: flex;
  flex-direction: row;
  gap: 8px;
}

/* ── guide: a card someone was sent ─────────────────────────────────────── */
body.guide main {
  /* The page's vertical step: what stands between the mark, the title and the
     cards. The fork below reads both of these, so the rule keeps equal air on
     each side of it whatever the type does. */
  --guide-step: 28px;
  --guide-title: 22px;
  --guide-title-leading: 1.3;
  margin: 0 auto;
  width: 100%;
  max-width: 720px;
  padding: 56px 24px 64px;
  display: grid;
  gap: var(--guide-step);
}

/* The two titles on this page are one title: the page's, and the fork's below it.
   Same size, same weight, written once so they cannot drift apart. */
body.guide h1 {
  font-size: var(--guide-title);
  line-height: var(--guide-title-leading);
  font-weight: 600;
  text-align: center;
}

.guide-mark {
  display: block;
  justify-self: center;
  line-height: 0;
}

a.guide-mark:hover {
  opacity: 0.8;
}

/* ── a published summary ────────────────────────────────────────────────── */
/* The one page served to somebody with no session. It keeps the guide's type and
   spacing, then takes more of the viewport than a card would: a summary is read,
   and a wide screen has room for it. The mark is the same wordmark the public
   site uses; the copy control sits on the left of the sheet, the workstream chips
   on the right; both wrap rather than overflow on a phone. */
body.share main {
  max-width: 72rem;
}

body.share .guide-mark img {
  width: 152px;
  height: auto;
}

.share-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  width: 100%;
  min-width: 0;
}

.share-send {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-width: 0;
}

.share-bar .share-copy,
.share-send .share-copy {
  flex: 0 0 auto;
  gap: 8px;
}

.share-bar .share-copy svg,
.share-send .share-copy svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
}

.share-bar .share-copy .copy-done,
.share-send .share-copy .copy-done {
  display: none;
  stroke-width: 1.8;
  stroke-linecap: round;
}

.share-bar .share-copy.is-copied,
.share-send .share-copy.is-copied {
  color: var(--ok);
}

.share-bar .share-copy.is-copied .copy-idle,
.share-send .share-copy.is-copied .copy-idle {
  display: none;
}

.share-bar .share-copy.is-copied .copy-done,
.share-send .share-copy.is-copied .copy-done {
  display: block;
}

.share-bar .tags {
  flex: 1 1 16rem;
  justify-content: flex-end;
  margin-left: auto;
  min-width: 0;
}

@media (max-width: 599px) {
  .share-bar .tags {
    flex-basis: 100%;
    margin-left: 0;
  }

}

.share-read {
  width: 100%;
  padding: 28px 28px 32px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg2);
  text-align: start;
  overflow-wrap: anywhere;
}

/* A summary is read, not scanned: longer measure and more leading than a panel. */
.share-read .blk.md {
  font-size: 15px;
  line-height: 1.65;
}

.share-read .blk.md p,
.share-read .blk.md ul,
.share-read .blk.md ol,
.share-read .blk.md blockquote {
  margin-bottom: 12px;
}

.share-read .blk.md h2,
.share-read .blk.md h3,
.share-read .blk.md h4 {
  margin: 22px 0 8px;
  font-size: 17px;
}

/* The text starts at the top of the sheet, whatever it opens with. */
.share-read .blk.md h2:first-child,
.share-read .blk.md h3:first-child,
.share-read .blk.md h4:first-child {
  margin-top: 0;
}

.share-when {
  margin: 0;
  color: var(--fg3);
  font-size: 12.5px;
  text-align: center;
}

/* ── phone ──────────────────────────────────────────────────────────────── */
@media (max-width: 799px) {
  main {
    padding: 24px 16px 32px;
  }

  .col {
    max-width: none;
  }

  h1 {
    font-size: 22px;
    line-height: 1.2;
  }

  .head .title {
    flex-wrap: wrap;
  }

  .row {
    grid-template-columns: 1fr;
    gap: 8px;
    height: auto;
    padding: 12px 16px;
  }

  .panel > :not(.row, .disclosure) {
    padding: 14px 16px;
  }

  .row:has(.act) {
    grid-template-columns: 1fr;
  }

  .row .act {
    justify-content: flex-start;
  }

  .inline {
    flex-direction: column;
    align-items: stretch;
  }

  .inline .input,
  .row.open .inline .input,
  .row.open .inline .input.mono,
  .tbl td .input.mono {
    width: 100%;
  }

  .inline .btn {
    align-self: flex-start;
  }

  /* Too narrow for the line to sit beside the tabs: Connect takes the stacked shape
     the design draws on a phone. */
  .connect {
    display: block;
    padding: 0;
    min-height: 0;
  }

  .connect-tabs {
    display: flex;
    align-items: center;
    height: auto;
    padding: 6px 6px 0;
    background: transparent;
    border-radius: 0;
  }

  .connect .tab {
    height: 26px;
    line-height: 26px;
    padding: 0 10px;
    font-size: 12px;
    background: transparent;
    box-shadow: none;
  }

  .connect .tab.on {
    background: var(--bg3);
    color: var(--fg);
    box-shadow: none;
  }

  .connect-line {
    height: 44px;
    padding: 0 6px 0 14px;
    gap: 8px;
  }

}

/* ── tags ───────────────────────────────────────────────────────────────── */
/* Each tag is the one chip face (VIEW.md §8, theme.css `--chip-*`): neutral fill, label in
   `--fg2`, colour only in a leaf's dot. The one place a value from the database reaches the
   stylesheet: `--tag` is the workspace's own colour for a tag, validated as a hex triple
   before it is written. The database gives the hue, the theme gives `--tag-l`, the
   lightness that reads on this page, so one stored colour is legible in both. Without a
   parseable colour the dot takes the neutral tone, so a tag the page cannot colour is still
   a tag. */
.tag {
  --tag: var(--fg3);
  --ink: oklch(from var(--tag) var(--tag-l) c h);
  display: inline-flex;
  align-items: center;
  gap: var(--chip-gap);
  height: var(--chip-height);
  padding: 0 var(--chip-pad);
  border-radius: var(--chip-radius);
  background: color-mix(in oklab, var(--fg) var(--chip-wash), transparent);
  color: var(--fg2);
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}

.tag.leaf::before {
  content: "";
  flex: none;
  width: var(--chip-icon);
  height: var(--chip-icon);
  background: radial-gradient(circle closest-side, var(--ink) 55%, transparent 60%);
}

/* One line that scrolls sideways, never a wrap (VIEW.md §8). */
.tags {
  display: flex;
  gap: 6px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

/* Tags on one thing as stacks (TAGS.md §6): each chip after the first runs `--tuck` under
   the one before it and is cut `--gap` clear of that one's end, through to the row, so a
   stack reads on a plain, hovered or selected row alike. `--tuck` is the apps' 0.8 of the
   chip's height. */
.tag-stack {
  --tuck: calc(0.8 * var(--chip-height));
  --gap: 2px;
  display: inline-flex;
  flex: none;
  align-items: center;
  font-size: 12px;
}

.tag-stack > * {
  position: relative;
}

.tag-stack > .tucked {
  margin-left: calc(-1 * var(--tuck));
}

.tag.tucked {
  padding-left: calc(var(--tuck) + var(--gap) + var(--chip-pad));
  mask: linear-gradient(to right, transparent calc(var(--tuck) + var(--gap)), #000 0);
}

.blk {
  min-width: 0;
}

.blk.md:not(.weave) { line-height: calc(1.45em - 2px); }

.blk.md:not(.weave) p { margin-bottom: 12px; }

.blk.md h2,
.blk.md h3,
.blk.md h4,
.blk.md h5,
.blk.md h6 {
  margin: 14px 0 6px;
  font-size: 15px;
  font-weight: 600;
}

.blk.md > :first-child {
  margin-top: 0;
}

.blk.md > :last-child {
  margin-bottom: 0;
}

.blk.md p,
.blk.md ul,
.blk.md ol,
.blk.md blockquote,
.blk.md pre,
.blk.md .tbl {
  margin: 0 0 8px;
}

.blk.md ul,
.blk.md ol {
  padding-left: 20px;
}

.blk.md li {
  margin: 2px 0;
}

.blk.md li > ul,
.blk.md li > ol {
  margin: 2px 0 0;
}

/* A task is its box and its words, not a bullet beside a box. */
.blk.md li:has(> input[type="checkbox"]),
.blk.md li:has(> p > input[type="checkbox"]) {
  list-style: none;
}

.blk.md li input[type="checkbox"] {
  margin: 0 8px 0 -20px;
  vertical-align: -1px;
  accent-color: var(--accent);
}

.blk.md blockquote {
  padding: 2px 0 2px 12px;
  border-left: 2px solid var(--line2);
  color: var(--fg2);
}

.blk.md hr {
  margin: 12px 0;
  border: 0;
  border-top: 1px solid var(--line);
}

.blk.md :not(pre) > code {
  padding: .1em .35em;
  border-radius: 5px;
  background: var(--bg3);
  font-family: var(--mono);
  font-size: .86em;
}

.blk.md img {
  display: inline-block;
  vertical-align: top;
}

/* Code is never wrapped: a long line scrolls inside its own block, and long machine
   output is held to a height, so the page keeps its own scroll. */
.blk pre {
  max-height: 420px;
  margin: 0;
  padding: 12px 16px;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
  color: var(--fg2);
  font: 12.5px/1.6 var(--mono);
  white-space: pre;
}

.blk pre code {
  padding: 0;
  background: none;
  font: inherit;
}

/* Without script the language heads its block; `copy.js` replaces that with a header. */
.blk pre[data-lang]:not(.has-head)::before {
  content: attr(data-lang);
  display: block;
  margin-bottom: 6px;
  color: var(--fg3);
  font-size: 11px;
  text-transform: uppercase;
}

/* A code block's header (`copy.js`): its language, and a control that copies its source.
   The frame moves from the block to the box holding both. */
.code-box {
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
}

.blk.md .code-box:not(:last-child) {
  margin: 0 0 8px;
}

.blk .code-box pre {
  margin: 0;
  border: 0;
  border-radius: 0;
}

.code-box summary {
  padding: 0 16px 8px;
}

.code-head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 4px 8px 4px 16px;
  border-bottom: 1px solid var(--line);
  -webkit-user-select: none;
  user-select: none;
}

.code-lang {
  color: var(--fg3);
  font: 600 11px/1 var(--sans);
  letter-spacing: .04em;
  text-transform: uppercase;
}

.code-copy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  padding: 3px 8px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--fg3);
  font: 500 12px/1.4 var(--sans);
  cursor: pointer;
}

.code-copy:hover {
  background: var(--bg3);
  color: var(--fg);
}

.code-copy:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.code-copy svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
}

.code-copy .copy-done {
  display: none;
  stroke-width: 1.8;
  stroke-linecap: round;
}

.code-copy.is-copied {
  color: var(--ok);
}

.code-copy.is-copied .copy-idle {
  display: none;
}

.code-copy.is-copied .copy-done {
  display: block;
}

.blk .tbl {
  max-height: 420px;
  overflow: auto;
}

.blk .tbl table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.blk .tbl th,
.blk .tbl td {
  padding: 6px 12px 6px 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}

.blk .tbl th {
  color: var(--fg3);
  font-size: 12px;
  font-weight: 600;
}

.blk .tbl .center {
  text-align: center;
}

.blk .tbl .right {
  text-align: right;
}

/* A picture in place is a preview: a phone screenshot at the width of the column is a
   page of scrolling between a person and the question. The apps draw the same bound. */
.blk img {
  max-width: 100%;
  max-height: 320px;
  width: auto;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 10px;
}

textarea.input.lines {
  width: 320px;
  height: auto;
  padding: 8px 10px;
  line-height: 1.45;
  resize: vertical;
}

/* ── a warning action ───────────────────────────────────────────────────── */
.btn.warn {
  border-color: var(--warn);
  background: var(--warn);
  color: var(--accent-ink);
}

.btn.warn:not(:disabled):hover {
  border-color: var(--warn);
  filter: brightness(1.06);
}

/* ── the sign-in page ───────────────────────────────────────────────────── */
/* The one bare document that is not a centred column: the ask on the left, what is
 * being joined on the right, each half the window and the whole of its height. Every
 * rule below is written under `body.bare` — partly so the signed-in pages cannot be
 * reached by it, partly because `body.bare p` would otherwise out-specify the tones
 * chosen here. The `:has` leaves the other bare pages — the second factor, a consent,
 * a pairing code — on the column they already have.
 */
body.bare main:has(> .auth) {
  max-width: none;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  gap: 0;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto 1fr;
  justify-items: stretch;
}

body.bare main:has(> .auth) > .brand {
  grid-area: 1 / 1;
  margin: 0;
  padding: 40px 56px 0;
}

body.bare .auth {
  grid-area: 2 / 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 40px 56px;
}

/* Auto margins take the free space above and below the ask, so it sits on the panel's
   centre line while the legal note keeps the floor. */
body.bare .auth-ask {
  margin-block: auto;
  display: grid;
  gap: 12px;
  width: 100%;
  max-width: 380px;
}

body.bare .auth-title {
  font-size: 40px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

body.bare .auth-sub {
  color: var(--fg2);
  font-size: 15px;
}

body.bare .auth-ways {
  display: grid;
  gap: 10px;
  margin-top: 12px;
}

body.bare .auth-ways .btn {
  height: 48px;
  border-radius: 10px;
  font-size: 15px;
}

/* The second provider is the same offer, not a fallback: it keeps its outline and takes
   no fill, so neither button reads as the lesser one. */
body.bare .auth-ways .btn:not(.primary) {
  background: transparent;
}
body.bare .auth-ways .btn { display: flex; align-items: center; justify-content: center; gap: 12px; }
body.bare .auth-ways svg { width: 20px; height: 20px; flex: none; }
body.bare .auth-ways svg { background: white; padding: 3px; border-radius: 50%; box-sizing: content-box; }
body.bare .pricing-review { width: min(560px, 100%); text-align: left; }
body.bare .pricing-review h1 { font-size: 36px; margin-bottom: 24px; }
body.bare .pricing-review ul { padding-left: 24px; margin: 24px 0; line-height: 2; }
body.bare .pricing-review form { margin-top: 24px; }

/* The invite code sits above the ways in, as part of their one form; `form`'s own grid
   on bare pages starts its items, so these ways stretch again. */
body.bare form.auth-ways { justify-items: stretch; }
body.bare .auth-code { display: grid; gap: 6px; color: var(--fg2); font-size: 13px; }
body.bare .auth-code input.code { width: 100%; }

/* Invite-only, said quietly beneath the ways in. */
body.bare .auth-invite {
  display: grid;
  gap: 4px;
  margin-top: 8px;
  color: var(--fg2);
  font-size: 13px;
  overflow-wrap: anywhere;
}

body.bare .auth-legal {
  max-width: 380px;
  margin-top: 32px;
  color: var(--fg3);
  font-size: 12px;
}

/* The panel carries no padding of its own: the field is a band across its whole floor,
   edge to edge, so only the copy above it is inset. */
body.bare .auth-why {
  grid-area: 1 / 2 / 3 / 3;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--bg2);
  border-left: 1px solid var(--line);
}

/* `1 0 auto`: the copy takes the free height and never gives any back, so the band is
   the only thing a short window can take from. */
body.bare .auth-props {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 36px;
  padding: 56px 56px 40px;
}

body.bare .auth-prop {
  display: grid;
  gap: 6px;
  max-width: 46ch;
}

body.bare .auth-tag {
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

body.bare .auth-claim {
  color: var(--fg);
  font-size: 17px;
  font-weight: 700;
}

body.bare .auth-note {
  color: var(--fg2);
  font-size: 14px;
}

/* The dog is a band, not a decoration: the full width of the panel, flush to its floor
   and both its sides, with the horizon reading as continuing past them. It is the last
   item of the panel's own column, so it cannot reach the words above it at any width,
   and the copy above it never gives height back (`1 0 auto`).

   The band is what a short window takes from: `max-height` hands it whatever the copy
   leaves, and `cover` from the bottom edge spends that on the drawing's sky. The `21vw`
   floor is where the sky runs out — under half the panel's width, against a drawing
   whose sky is its top fifth — so the crop never reaches the dog's ears; a window too
   short even for that scrolls rather than cutting into the animal. */
body.bare .auth-art {
  flex: 0 1 auto;
  min-height: 0;
  display: block;
  width: 100%;
  height: auto;
  max-height: max(21vw, 100vh - 420px);
  object-fit: cover;
  object-position: center bottom;
}

@media (max-width: 1100px) {
  body.bare main:has(> .auth) > .brand {
    padding: 32px 32px 0;
  }

  body.bare .auth {
    padding: 32px;
  }

  body.bare .auth-props {
    padding: 40px 32px 32px;
  }

}

/* Too narrow for two columns: one column, and the field goes under the buttons. Nobody
   scrolls past a dog to sign in. */
@media (max-width: 799px) {
  body.bare main:has(> .auth) {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto;
  }

  body.bare main:has(> .auth) > .brand {
    padding: 28px 24px 0;
  }

  /* The ask still owns the first screen: the panel is the window less the mark above
     it, so the auto margins centre the block and the field starts at the fold. */
  body.bare .auth {
    grid-area: 2 / 1;
    min-height: calc(100vh - 96px);
    padding: 24px 24px 32px;
  }

  body.bare .auth-ask,
  body.bare .auth-legal {
    max-width: 440px;
  }

  body.bare .auth-why {
    grid-area: 3 / 1;
    border-left: 0;
    border-top: 1px solid var(--line);
  }

  body.bare .auth-props {
    gap: 28px;
    padding: 40px 24px 32px;
  }

  /* Stacked, the band is already past the fold and the page scrolls anyway: it is drawn
     whole, across the whole width. */
  body.bare .auth-art {
    max-height: none;
  }

}

pre.mermaid { background: transparent; text-align: center; white-space: pre-wrap; }

