/* The same tokens the landing page uses. Two surfaces, one product: an app in a
   different palette from its own marketing site reads as two companies. */
:root {
  /* The reading measure for the desk, and the page gutter. One column means one
     number to get right, and everything on the screen measures from it. */
  --measure: 820px;
  --gutter: 24px;
  --ink: #0a0a0a;
  --ink-2: #3d3d3d;
  --ink-3: #6b6b6b;
  --ink-4: #9b9b9b;
  --line: #e6e6e6;
  --line-2: #f1f1f1;
  --hover: #f0f0f1;
  --bg: #f4f4f5;
  --bg-2: #ebebed;
  --card: #ffffff;
  --accent: #1a5cff;
  --accent-soft: #eef3ff;
  --good: #0f7a4a;
  --good-soft: #e9f6ef;
  --warn: #9a6206;
  --warn-soft: #fdf4e6;
  --bad: #b02a2a;
  --bad-soft: #fdeeee;

  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 8px;
  --r-xl: 12px;
  --r-2xl: 16px;
  --r-3xl: 24px;
  --radius: var(--r-xl);
  --ease: cubic-bezier(0.32, 0.72, 0, 1);

  /* Flat by default. A shadow on every card is the single loudest tell that an
     interface was assembled rather than designed. */
  --shadow: none;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.5 'Manrope', -apple-system, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ---------------------------------------------------------------- topbar */

.topbar {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 22px;
  height: 60px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 20;
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 210px; }

.mark { width: 32px; height: 32px; border-radius: 8px; background: var(--ink); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 12px; letter-spacing: .5px; }

.brand-name { font-weight: 650; font-size: 14px; line-height: 1.1; }
.brand-sub { font-size: 11.5px; color: var(--ink-3); }

.tabs { display: flex; gap: 2px; flex: 1; }

.tab {
  border: 0;
  background: transparent;
  color: var(--ink-2);
  padding: 7px 12px;
  border-radius: 7px;
  font: inherit;
  font-size: 13.5px;
  cursor: pointer;
  display: flex; align-items: center; gap: 6px;
}
.tab:hover { background: var(--line-2); color: var(--ink); }
.tab.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

.pill {
  background: var(--ink);
  color: #fff;
  border-radius: 20px;
  font-size: 11px;
  padding: 1px 7px;
  font-weight: 600;
}
.tab.active .pill { background: var(--accent); }

.topbar-right { display: flex; align-items: center; gap: 10px; }

.badge {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .6px;
  font-weight: 650;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 6px;
  padding: 4px 9px;
}

.banner {
  padding: 10px 22px;
  background: var(--accent-soft);
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  color: var(--accent);
}
.banner.error { background: var(--bad-soft); color: var(--bad); }

/* ---------------------------------------------------------------- layout */

/* The console's page. `main:not(.workspace)` because the desk's <main> is the
   two-pane workspace and does its own spacing — a bare `main` selector caught
   both, which is where the desk's fourteen-pixel phone inset was coming from,
   along with a 1500px width cap it never asked for. */
main:not(.workspace):not(.column) { padding: 22px; max-width: 1500px; margin: 0 auto; }

.view { display: none; }
.view.active { display: block; }

.panel {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 18px;
  box-shadow: var(--shadow);
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.panel-head h2 { font-size: 15px; margin: 0; font-weight: 650; letter-spacing: -.1px; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 1050px) { .grid-2 { grid-template-columns: 1fr; } }

.muted { color: var(--ink-3); font-size: 12.5px; }
.small { font-size: 12px; }
.row { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }

/* ---------------------------------------------------------------- kpis */

.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}

.kpi {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 15px 16px;
  box-shadow: var(--shadow);
}
.kpi .label {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .55px;
  color: var(--ink-3);
  font-weight: 600;
  margin-bottom: 7px;
}
.kpi .value { font-size: 25px; font-weight: 680; letter-spacing: -.6px; line-height: 1.1; }
.kpi .sub { font-size: 12px; color: var(--ink-3); margin-top: 5px; }
.kpi.alert { border-color: #f0cfcd; background: linear-gradient(180deg, #fff, var(--bad-soft)); }
.kpi.alert .value { color: var(--bad); }
.kpi.good .value { color: var(--good); }

/* ---------------------------------------------------------------- buckets */

.buckets { display: flex; flex-direction: column; gap: 9px; }

.bucket { display: grid; grid-template-columns: 74px 1fr 116px; align-items: center; gap: 12px; }
.bucket .name { font-size: 12.5px; font-weight: 620; color: var(--ink-2); }
.bucket .bar-wrap { background: var(--line-2); border-radius: 5px; height: 22px; overflow: hidden; }
.bucket .bar { height: 100%; background: var(--accent); border-radius: 5px; min-width: 2px; transition: width .3s; }
.bucket.hot .bar { background: var(--bad); }
.bucket.warm .bar { background: #c98a2e; }
.bucket .meta { font-size: 12px; color: var(--ink-3); text-align: right; }

/* ---------------------------------------------------------------- lists */

.mini-list { display: flex; flex-direction: column; }

.mini-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--line-2);
  cursor: pointer;
}
.mini-item:last-child { border-bottom: 0; }
.mini-item:hover { background: #fafbfc; }
.mini-item .score {
  width: 34px; height: 34px;
  border-radius: 8px;
  display: grid; place-items: center;
  font-weight: 680; font-size: 12.5px;
  background: var(--line-2); color: var(--ink-2);
  flex: none;
}
.mini-item .score.hot { background: var(--bad-soft); color: var(--bad); }
.mini-item .score.warm { background: var(--warn-soft); color: var(--warn); }
.mini-item .body { flex: 1; min-width: 0; }
.mini-item .t { font-size: 13px; font-weight: 560; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mini-item .s { font-size: 11.5px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.empty { color: var(--ink-3); font-size: 13px; padding: 16px 2px; }

/* ---------------------------------------------------------------- queue */

/* .queue and .qitem styles removed: superseded by the Today list. */


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

.btn {
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  border-radius: 7px;
  padding: 7px 13px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  font-weight: 540;
}
.btn:hover { background: var(--line-2); }
.btn.primary { background: var(--ink); border-color: var(--ink); color: #fff; }
.btn.primary:hover { background: #1d2833; }
.btn.good { background: var(--good); border-color: var(--good); color: #fff; }
.btn.small { padding: 5px 10px; font-size: 12px; }
.btn:disabled { opacity: .5; cursor: default; }

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

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 12.8px; }
th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--ink-3);
  font-weight: 650;
  padding: 9px 10px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
td { padding: 9px 10px; border-bottom: 1px solid var(--line-2); vertical-align: top; }
tr:hover td { background: #fafbfc; }
.num { text-align: right; font-variant-numeric: tabular-nums; }

.search {
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 7px 11px;
  font: inherit;
  font-size: 13px;
  min-width: 280px;
}

/* ---------------------------------------------------------------- coi */

.coi-list { display: flex; flex-direction: column; gap: 10px; }
.coi {
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 13px 15px;
}
.coi-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.coi-head .holder { font-weight: 580; font-size: 14px; }
.checks { margin-top: 10px; display: flex; flex-direction: column; gap: 5px; }
.check { display: flex; gap: 9px; font-size: 12.5px; align-items: flex-start; }
.check .ico { width: 15px; flex: none; font-weight: 700; }
.check.ok .ico { color: var(--good); }
.check.bad .ico { color: var(--bad); }
.check.warn .ico { color: var(--warn); }

/* ---------------------------------------------------------------- forms */

.form { display: flex; flex-direction: column; gap: 9px; }
.form input, .search, #csv-text {
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 9px 11px;
  font: inherit;
  font-size: 13.5px;
  width: 100%;
}
/* The Import view's paste box, named rather than left as a bare `textarea`
   rule that would style every textarea on the page. */
#csv-text { min-height: 150px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; margin-top: 12px; }

.result { margin-top: 14px; font-size: 13px; }
.result pre {
  background: #f8f9fb;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px;
  overflow-x: auto;
  font-size: 12px;
  margin: 0;
}

/* ---------------------------------------------------------------- events */

.events { display: flex; flex-direction: column; }
.event { display: flex; gap: 12px; padding: 8px 2px; border-bottom: 1px solid var(--line-2); font-size: 12.8px; }
.event:last-child { border-bottom: 0; }
.event .when { color: var(--ink-3); min-width: 132px; font-variant-numeric: tabular-nums; font-size: 12px; }
.event .kind { color: var(--accent); font-weight: 600; min-width: 130px; }

/* ---------------------------------------------------------------- drawer */

/* `[hidden]` in the UA sheet is only display:none, and any author rule that
   sets display wins over it. Without this the overlay covers the app at all
   times, dimming everything behind it. */
.drawer[hidden] { display: none; }

.drawer {
  position: fixed; inset: 0;
  background: rgba(15, 23, 32, .38);
  z-index: 60;
  display: flex; justify-content: flex-end;
}
.drawer-inner {
  width: min(680px, 96vw);
  background: var(--card);
  height: 100%;
  overflow-y: auto;
  padding: 26px;
  position: relative;
  box-shadow: -8px 0 28px rgba(15,23,32,.14);
}
.drawer-close {
  position: absolute; top: 14px; right: 16px;
  border: 0; background: transparent; font-size: 26px; line-height: 1;
  cursor: pointer; color: var(--ink-3);
}
.drawer-close:hover { color: var(--ink); }

.draft {
  background: #f8f9fb;
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 15px;
  white-space: pre-wrap;
  font-size: 13px;
  line-height: 1.58;
  font-family: inherit;
}
.draft-subject { font-weight: 620; margin-bottom: 4px; font-size: 13.5px; }
.draft-to { font-size: 12px; color: var(--ink-3); margin-bottom: 12px; }
.drawer h3 { font-size: 17px; margin: 0 0 6px; letter-spacing: -.2px; }
.drawer .meta-line { font-size: 12.5px; color: var(--ink-3); margin-bottom: 16px; }
.drawer h4 { font-size: 12px; text-transform: uppercase; letter-spacing: .55px; color: var(--ink-3); margin: 20px 0 9px; }

/* ---------------------------------------------------------------- connections */

.coverage { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.coverage-num { font-size: 44px; line-height: 1; font-weight: 700; letter-spacing: -1.5px; }
.coverage-num span { font-size: 20px; color: var(--ink-3); margin-left: 2px; }
.coverage-body { flex: 1; min-width: 260px; }
.coverage-bar { height: 8px; background: var(--line-2); border-radius: 999px; overflow: hidden; margin-bottom: 10px; }
.coverage-bar span { display: block; height: 100%; background: var(--good); border-radius: 999px; transition: width .4s; }
.coverage-note { font-size: 13.5px; line-height: 20px; color: var(--ink-2); }
.coverage-note b { color: var(--ink); }

.conn-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; }

.conn {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 15px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.conn-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.conn-name { font-size: 14px; font-weight: 640; }
.conn-cat { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.conn-blurb { font-size: 12.5px; line-height: 18px; color: var(--ink-2); }
.conn-unlocks { display: flex; flex-wrap: wrap; gap: 5px; }
.conn-unlocks span {
  font-size: 11px;
  background: var(--line-2);
  border-radius: 5px;
  padding: 3px 7px;
  color: var(--ink-2);
}
.conn-actions { display: flex; gap: 8px; align-items: center; margin-top: auto; padding-top: 4px; }
.conn-note { font-size: 12px; line-height: 17px; color: var(--warn); }

.conn.connected { border-color: #bfe3cd; background: linear-gradient(180deg, #fff, var(--good-soft)); }
.conn.pending { border-color: #e8dcc0; }
.conn.error { border-color: #f0cfcd; }

[data-theme="dark"] .conn.connected { background: #16281d; border-color: #245139; }
[data-theme="dark"] .conn.pending { border-color: #3a3320; }
[data-theme="dark"] .conn-unlocks span { background: #272727; }

.conn-logo { width: 30px; height: 30px; flex: none; display: block; }
.conn-logo svg { width: 30px; height: 30px; display: block; }
.conn-logo-plain {
  display: grid; place-items: center; border-radius: 8px;
  background: var(--line-2); color: var(--ink-3);
  font-weight: 700; font-size: 14px;
}
.conn-head { flex: 1; min-width: 0; }
.conn-top { align-items: center; }

.profile { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.profile-body { flex: 1; min-width: 240px; }
.profile-label { font-size: 16px; font-weight: 650; letter-spacing: -.3px; }
.profile-reason { font-size: 12.5px; line-height: 18px; color: var(--ink-3); margin-top: 3px; }

.traits { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; }
.traits li { font-size: 13px; line-height: 19px; color: var(--ink-2); padding-left: 16px; position: relative; }
.traits li:before { content: ''; position: absolute; left: 0; top: 8px; width: 6px; height: 6px; border-radius: 999px; background: var(--good); }
.traits b { color: var(--ink); font-weight: 640; }

/* ============================================================
   TODAY, DETAIL AND RESPONSIVE SHELL
   Today leads, and every row opens onto what was actually done.
   ============================================================ */

.mark {
  width: 30px; height: 30px; border-radius: 0; background: none;
  display: block; flex: none;
  /* The logo is an SVG with its own fills. The initials square this replaced
     drew with type, and its colour and tracking would otherwise still apply. */
  color: inherit; font-size: 0; font-weight: 400; letter-spacing: normal; line-height: 0;
  place-items: center;
}
.mark svg { width: 30px; height: 30px; display: block; }

.progress { height: 3px; background: var(--line-2); border-radius: 999px; overflow: hidden; margin-bottom: 18px; }
.progress span { display: block; height: 100%; background: var(--good); border-radius: 999px; transition: width .5s var(--ease, ease); width: 4%; }

.today { list-style: none; margin: 0; padding: 0; }

.today li {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  margin-bottom: 8px;
  cursor: pointer;
  background: var(--card);
  transition: border-color 400ms ease, background 400ms ease;
}
.today li:last-child { margin-bottom: 0; }
.today li:hover { border-color: #cbd5dd; background: #fafbfc; }

.today .t-title { font-size: 14px; line-height: 20px; font-weight: 620; }
.today .t-sub { font-size: 12px; line-height: 17px; color: var(--ink-3); margin-top: 2px; }

.task-mark {
  width: 26px; height: 26px; border-radius: 999px;
  background: var(--good-soft); display: grid; place-items: center; flex: none;
}
.task-mark svg { width: 15px; height: 15px; }
.task-mark path { stroke: var(--good); stroke-width: 2.6; fill: none; stroke-linecap: round; stroke-linejoin: round; }

/* ---------------------------------------------------------------- detail */

.detail {
  position: fixed; inset: 0;
  background: rgba(15, 23, 32, .38);
  z-index: 70;
  display: flex; justify-content: flex-end;
}
.detail[hidden] { display: none; }
.detail-inner {
  width: min(580px, 100%);
  background: var(--card);
  height: 100%;
  overflow-y: auto;
  padding: 28px 26px 40px;
  position: relative;
  box-shadow: -8px 0 28px rgba(15, 23, 32, .14);
}
.detail-close {
  position: absolute; top: 16px; right: 18px;
  border: 0; background: transparent; font-size: 26px; line-height: 1;
  cursor: pointer; color: var(--ink-3);
}
.detail-close:hover { color: var(--ink); }
.detail h3 { font-size: 19px; line-height: 26px; margin: 0 0 6px; letter-spacing: -.3px; }
.detail h4 {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .55px;
  color: var(--ink-3); margin: 24px 0 10px; font-weight: 680;
}
.detail .facts { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: 13px; color: var(--ink-2); }
.detail .facts b { color: var(--ink); font-weight: 620; }
.detail .why { font-size: 13.5px; line-height: 21px; color: var(--ink-2); }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { position: relative; padding: 0 0 18px 22px; border-left: 2px solid var(--line); }
.timeline li:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline li:before {
  content: ''; position: absolute; left: -6px; top: 3px;
  width: 10px; height: 10px; border-radius: 999px;
  background: var(--line); border: 2px solid var(--card);
}
.timeline li.sent:before, .timeline li.closed:before { background: var(--good); }
.timeline .tl-text { font-size: 13px; line-height: 19px; }
.timeline .tl-when { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }

.draft-box {
  background: #f8f9fb; border: 1px solid var(--line); border-radius: 10px;
  padding: 15px; white-space: pre-wrap; font-size: 13px; line-height: 1.55;
}
.detail-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 24px; }

/* ---------------------------------------------------------------- responsive

   The console's chrome was laid out for a phone three times: once when it had a
   wrapped two row header, once to give the brand its room, and once for the
   header it actually has. Each rewrite sat below the last, so the later one won
   on every property they shared and the earlier ones stopped doing anything —
   except for the handful of properties nobody had restated since.

   Nobody could tell which was which by reading it. What is here is the set that
   still reaches the screen: the surviving declarations of all three, in one
   place, in the order that keeps each of them winning. */

@media (max-width: 900px) {
  /* Only the lines that still reach the screen. The header states its own
     height, padding and gap further down and wins on all three, so restating
     them here did nothing; what survives is everything the header never says —
     that it wraps, and that the brand's name and subtitle do not. */
  .topbar { flex-wrap: wrap; }
  .brand { flex: 1 1 auto; }
  .brand > div { min-width: 0; }
  .brand-name { white-space: nowrap; }
  .brand-sub { display: none; }
  .badge { display: none; }
  .plan-badge { display: none; }
  .topbar-right .btn { padding: 7px 13px; font-size: 13px; }

  /* The sections become one scrolling row. A fade on the right edge says there
     are more of them without a scrollbar saying it. */
  .tabs {
    order: 3; flex: 1 0 100%; width: 100%; gap: 2px;
    margin-top: 10px; padding-bottom: 9px; padding-right: 34px;
    overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, #000 84%, transparent);
    mask-image: linear-gradient(to right, #000 84%, transparent);
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { flex: none; white-space: nowrap; padding: 7px 11px; font-size: 13px; }
  .pill { font-size: 10.5px; padding: 1px 6px; }

  main:not(.workspace):not(.column) { padding: 14px; }
  .kpis { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .grid-2 { grid-template-columns: 1fr; }
  .conn-list { grid-template-columns: 1fr; }
  .coverage-num { font-size: 34px; }
}

@media (max-width: 560px) {
  .kpis { grid-template-columns: 1fr; }
  .kpi .value { font-size: 22px; }
  .today li { grid-template-columns: 26px 1fr; }
  .today .tag { display: none; }
  .detail-inner { width: 100%; padding: 24px 18px 40px; }
  .panel { padding: 15px; }
  .section h2, .panel-head h2 { font-size: 15px; }
}

@media (max-width: 420px) {
  .brand-sub { display: none; }
  .topbar-right .btn { padding: 7px 11px; }
}

/* ============================================================
   DARK MODE
   Same surface scale as the marketing site: page #000000,
   raised #181818, card on raised #1F1F1F, nested #272727.
   ============================================================ */

:root { color-scheme: light; }

[data-theme="dark"] {
  color-scheme: dark;
  --ink: #ffffff;
  --ink-2: #c4c4c4;
  --ink-3: #8a8a8a;
  --ink-4: #6a6a6a;
  --line: #2a2a2a;
  --line-2: #1c1c1c;
  --hover: #1f1f1f;
  --bg: #000000;
  --bg-2: #0d0d0d;
  --card: #141414;
  --accent: #5b86ff;
  --accent-soft: #101a33;
  --good: #3ddc84;
  --good-soft: #0f2018;
  --warn: #ffb86b;
  --warn-soft: #241c10;
  --bad: #ff8a80;
  --bad-soft: #241212;
  --shadow: none;
}

/* buttons: the primary inverts so it stays the strongest thing on screen */
[data-theme="dark"] .btn { background: #181818; border-color: var(--line); color: var(--ink); }
[data-theme="dark"] .btn:hover { background: #272727; }
[data-theme="dark"] .btn.primary { background: #ffffff; border-color: #ffffff; color: #000000; }
[data-theme="dark"] .btn.primary:hover { background: #e6e6e6; }
[data-theme="dark"] .btn.good { background: var(--good); border-color: var(--good); color: #0a0a0a; }
[data-theme="dark"] .btn.good:hover { background: #4fe694; }

/* surfaces that carried an explicit light background */
[data-theme="dark"] .mini-item:hover,
[data-theme="dark"] tr:hover td,
[data-theme="dark"] .today li:hover { background: #1f1f1f; }
[data-theme="dark"] .today li:hover { border-color: #3d3d3d; }
[data-theme="dark"] .drawer-inner,
[data-theme="dark"] .detail-inner { background: #181818; }
[data-theme="dark"] .draft-box,
[data-theme="dark"] .result pre { background: #1f1f1f; border-color: var(--line); }
[data-theme="dark"] .task-mark { background: #16281d; }
[data-theme="dark"] .ico { background: #1f1f1f; }

[data-theme="dark"] .form input,
[data-theme="dark"] #csv-text,
[data-theme="dark"] .search { background: #181818; border-color: var(--line); color: var(--ink); }

[data-theme="dark"] .coverage-bar { background: #272727; }
[data-theme="dark"] .mark { background: none; }
[data-theme="dark"] .foot-base,
[data-theme="dark"] .foot-col h4 { color: #8a8a8a; }

/* ---------------------------------------------------------------- toggle */

.theme-toggle {
  width: 34px; height: 34px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink-3);
  display: grid; place-items: center;
  cursor: pointer; padding: 0; flex: none;
  transition: background 200ms ease, color 200ms ease;
}
.theme-toggle:hover { background: var(--line-2); color: var(--ink); }
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.theme-toggle svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* One icon per theme. Both rules must be complete pairs: leaving the light
   state with no rule at all lets an earlier display:none win and the button
   renders empty in light mode. */
.theme-toggle .i-sun { display: none; }
.theme-toggle .i-moon { display: block; }
[data-theme="dark"] .theme-toggle .i-sun { display: block; }
[data-theme="dark"] .theme-toggle .i-moon { display: none; }

/* Count pills used var(--ink) as a background with hardcoded white text,
   which is white on white in dark mode. */
[data-theme="dark"] .pill { background: #ffffff; color: #000000; }
[data-theme="dark"] .tab.active .pill { background: var(--accent); color: #0a0a0a; }

/* ---------------------------------------------------------------- modal */

.modal {
  position: fixed; inset: 0;
  background: rgba(15, 23, 32, .42);
  z-index: 80;
  display: grid; place-items: center;
  padding: 20px;
}
.modal[hidden] { display: none; }

.modal-inner {
  width: 100%; max-width: 460px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 26px;
  position: relative;
  box-shadow: 0 18px 48px rgba(15, 23, 32, .22);
}
.modal-inner h3 { font-size: 18px; margin: 0 0 6px; letter-spacing: -.3px; }
.modal-sub { font-size: 13.5px; line-height: 20px; color: var(--ink-2); margin: 0 0 18px; }
.modal-label { display: block; font-size: 13px; font-weight: 620; margin-bottom: 6px; }
.modal-input {
  width: 100%; padding: 11px 13px;
  border: 1px solid var(--line); border-radius: 9px;
  font: inherit; font-size: 14px;
  background: var(--bg); color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.modal-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(31,95,139,.12); }
.modal-note { font-size: 12px; line-height: 17px; color: var(--ink-3); margin: 10px 0 0; }
.modal-err { font-size: 13px; color: var(--bad); margin: 12px 0 0; min-height: 18px; }
.modal-close {
  position: absolute; top: 14px; right: 16px;
  border: 0; background: transparent; font-size: 24px; line-height: 1;
  cursor: pointer; color: var(--ink-3);
}
.modal-close:hover { color: var(--ink); }

[data-theme="dark"] .modal-inner { background: #181818; }
[data-theme="dark"] .modal-input { background: #000000; }

/* a connected source can be re-read on demand */
.conn-meta { font-size: 11.5px; color: var(--ink-3); }

/* The mark draws itself, then the check closes the ring. Hover replays. */
.od-ring {
  stroke-dasharray: 70.8 17;
  animation: odRingDraw 900ms cubic-bezier(0.32, 0.72, 0, 1) both;
}
.od-tick {
  stroke-dasharray: 25;
  animation: odTickDraw 460ms cubic-bezier(0.32, 0.72, 0, 1) 620ms both;
}
@keyframes odRingDraw {
  from { stroke-dasharray: 0 88; }
  to   { stroke-dasharray: 70.8 17; }
}
@keyframes odTickDraw {
  from { stroke-dashoffset: 25; }
  to   { stroke-dashoffset: 0; }
}
.od-logo:hover .od-ring { animation: odRingDraw 620ms cubic-bezier(0.32, 0.72, 0, 1) both; }
.od-logo:hover .od-tick { animation: odTickDraw 380ms cubic-bezier(0.32, 0.72, 0, 1) 360ms both; }
.od-logo { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .od-ring, .od-tick,
  .od-logo:hover .od-ring, .od-logo:hover .od-tick { animation: none; }
  .od-tick { stroke-dasharray: none; stroke-dashoffset: 0; }
}

/* ============================================================
   BILLING
   ============================================================ */



/* ============================================================
   TAGS
   app.js has always emitted .tag / .tag.good / .tag.warm / .tag.hot
   and no rule ever existed for them, so every status chip on Today,
   COI, Book, Activity and Connections rendered as bare text.
   ============================================================ */

.tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .2px;
  line-height: 16px;
  white-space: nowrap;
  background: var(--line-2);
  color: var(--ink-2);
}
.tag.good { background: var(--good-soft); color: var(--good); }
.tag.warm { background: var(--warn-soft); color: var(--warn); }
.tag.hot  { background: var(--bad-soft);  color: var(--bad); }

/* ============================================================
   PORTALS
   ============================================================ */

.portal-list { display: grid; gap: 14px; }

.portal {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--line-2);
  padding: 16px;
}
.portal-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.portal-name { font-size: 14px; font-weight: 650; letter-spacing: -.1px; }
.portal-what { font-size: 12.5px; color: var(--ink-3); margin-top: 4px; line-height: 18px; }
.portal-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; margin-top: 12px; }
.portal-field { display: flex; flex-direction: column; gap: 5px; min-width: 200px; flex: 1; }
.portal-field label { font-size: 11px; font-weight: 600; letter-spacing: .3px; text-transform: uppercase; color: var(--ink-3); }
.portal-field input {
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
  font: inherit; font-size: 13px; background: var(--card); color: var(--ink);
}
.portal-field input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.portal-note { font-size: 12px; color: var(--ink-3); margin-top: 10px; line-height: 17px; }

.portal-result { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.portal-kv { display: grid; grid-template-columns: 160px 1fr; gap: 6px 14px; font-size: 13px; }
.portal-kv dt { color: var(--ink-3); }
.portal-kv dd { margin: 0; font-weight: 550; }

.portal-shot {
  margin-top: 12px; border: 1px solid var(--line); border-radius: 8px;
  overflow: hidden; max-width: 520px; background: var(--card);
}
.portal-shot img { display: block; width: 100%; height: auto; }

.portal-matches { margin: 12px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.portal-matches li {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  font-size: 13px; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--card);
}
.portal-matches .nm { font-weight: 600; }
.portal-matches .meta { color: var(--ink-3); font-size: 12px; }

/* ============================================================
   THE APP
   Two things: a list of what needs deciding, and a chat. On a
   phone they are two screens; on a wide screen they are two
   panes, because there is room and hiding one would be silly.
   ============================================================ */

body.shell {
  min-height: 100dvh;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

/* The desk is a screen, not a document.
 *
 * `min-height` alone leaves the page free to grow to the height of whatever is
 * inside it, and a conversation is tall. So the pane grew to six thousand
 * pixels, the thread never became a scroll container of its own, and the *page*
 * scrolled instead: opening the chat dropped the person at the bottom with the
 * top of the thread above the viewport, and the composer sat at the end of a
 * document rather than at the bottom of a screen.
 *
 * Every other surface here — the marketing page, the operator's numbers — is a
 * document and should keep scrolling like one, which is why this is on the desk
 * rather than on the shell. */
body.desk {
  height: 100dvh;
  overflow: hidden;
}

/* ------------------------------------------------------------ the bar */

.topbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; height: 60px; padding: 0 26px;
  /* No rule under it. The header is a strip of the page, and the panes below
     start at their own edge — a line here was drawing a border around nothing. */
  background: color-mix(in srgb, var(--bg) 84%, transparent);
  backdrop-filter: saturate(1.6) blur(14px);
  /* Stated, not merely omitted: an earlier rule for this class set a border and
     a rule that does not mention it leaves it in place. */
  border-bottom: 0;
}
.brand { display: flex; align-items: center; gap: 11px; min-width: 0; }
.brand-name { font-size: 14px; font-weight: 700; letter-spacing: -.2px; line-height: 1.2; }
.brand-sub {
  font-size: 12px; color: var(--ink-3); line-height: 1.3; margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 46vw;
}
.topbar-right { display: flex; align-items: center; gap: 8px; flex: none; }

.badge {
  font-size: 11px; font-weight: 700; letter-spacing: .7px;
  color: var(--ink-3); background: var(--line-2);
  border-radius: var(--r-md); padding: 5px 9px; cursor: pointer;
}
.badge:hover { color: var(--ink); }



/* ------------------------------------------------------------ the frame */

.workspace { flex: 1; display: grid; grid-template-columns: 1fr; min-height: 0; min-width: 0; }

/* Grid and flex items default to a minimum of their content, so one long
   unbreakable line inside a message is enough to push a whole pane wider than
   the phone it is on. Zeroing the minimum is what makes the pane obey the
   screen instead of its own contents. */
.workspace > *, .screen > * { min-width: 0; }

.screen { display: none; min-height: 0; }
.screen.active { display: flex; flex-direction: column; }

/* On a phone the two screens take turns and a bar at the bottom says which. */
.switch {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; justify-content: center; gap: 4px;
  padding: 6px 10px calc(6px + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  border-top: 1px solid var(--line);
}
.switch-item {
  flex: 1; max-width: 220px;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink-3);
  border-radius: var(--r-lg);
  transition: color 200ms var(--ease), background 200ms var(--ease);
}
.switch-item svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.switch-item.active { color: var(--accent); background: var(--accent-soft); }
.pip-count { font-variant-numeric: tabular-nums; opacity: .75; margin-left: 6px; }

body.shell { padding-bottom: 62px; }

/* ------------------------------------------------------------ the list

   These rows are the landing page's mock, made real: a mark, two lines of
   text, and a tag on the right. Same columns, same radii, same type — because a
   desk that looks unlike the thing being advertised reads as a worse product
   than the one that was sold. */

/* The bar the landing page shows, doing what it implies: what the desk has
   closed today against what is left. No animation — that belongs on a page
   selling the thing, not on the thing. */
.progress {
  height: 3px; background: var(--line-2); border-radius: 999px;
  overflow: hidden;
  /* Stated: the classic console's rule for this class set a bottom margin of
     its own, and a rule that does not mention it leaves it in place. */
  margin: 22px 24px 0;
  margin-bottom: 0;
}
.progress span {
  display: block; height: 100%; width: 0;
  background: var(--good); border-radius: 999px;
  transition: width 500ms var(--ease);
}

/* The heading is the heading. What the desk has done, what is left, and the way
   to act on all of it go underneath it.

   Three things on one baseline worked on a wide pane and fell apart on a phone,
   where the counts wrapped mid-phrase and the Select button got pushed around by
   however long the sentence happened to be that minute. A heading owns its line;
   the numbers are a line of their own. */
.list-head {
  display: block;
  padding: 14px 24px 14px;
}
.list-head h1 { font-size: 18px; line-height: 28px; font-weight: 700; letter-spacing: -.2px; margin: 0; }
.list-sub {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 6px;
}
.list-meta { font-size: 12px; line-height: 16px; color: var(--ink-3); min-width: 0; }

.groups { list-style: none; margin: 0; padding: 0 0 44px; }
.group { list-style: none; }
.rows { list-style: none; margin: 0; padding: 0; }

.group-head {
  padding: 24px 24px 10px;
  font-size: 11px; font-weight: 700; letter-spacing: .9px; text-transform: uppercase;
  color: var(--ink-4);
}
.group-head .pip-count { margin-left: 7px; font-weight: 600; letter-spacing: 0; }
.group:first-child .group-head { padding-top: 0; }

/* The row is the landing page's row. Same columns, same radius, same padding,
   same type, same tag — because the page is a promise about this screen, and a
   desk that looks unlike the thing being sold is a promise broken quietly. */
.task {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 13px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  margin: 0 10px 8px;
  background: var(--card);
  transition: border-color 160ms var(--ease);
}
.task:hover { border-color: var(--ink-4); }
.group:last-child .task:last-child { margin-bottom: 0; }

.task-mark {
  width: 26px; height: 26px; border-radius: 999px;
  display: grid; place-items: center; flex: none;
  background: var(--good-soft);
}
.task-mark svg { width: 15px; height: 15px; }
.task-mark path { stroke: var(--good); stroke-width: 2.6; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.task-mark.late { background: var(--warn-soft); }
.task-mark.late path { stroke: var(--warn); }
.task-mark.open { background: var(--line-2); }
.task-mark.open path { stroke: var(--ink-4); }

.task-open {
  display: block; width: 100%; min-width: 0; text-align: left; padding: 0;
  background: none; border: 0; font: inherit; color: inherit; cursor: pointer;
}
.task-label { display: block; font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: -.1px; }
.task-sub { display: block; font-size: 12px; line-height: 16px; color: var(--ink-3); margin-top: 2px; font-weight: 400; }

/* Both live in one cell so the swap cannot move anything. */
.task-end { display: grid; place-items: center; }
.task-end > * { grid-area: 1 / 1; }

.task-tag {
  font-size: 12px; line-height: 16px; font-weight: 600;
  padding: 4px 8px; border-radius: var(--r-md);
  background: var(--line-2); color: var(--ink-2); white-space: nowrap;
}
.task-tag.late { background: var(--warn-soft); color: var(--warn); }

.task-act {
  font: inherit; font-size: 12px; font-weight: 600; line-height: 16px;
  padding: 4px 11px; border-radius: var(--r-md); white-space: nowrap; cursor: pointer;
  border: 1px solid var(--accent); background: var(--accent); color: #fff;
  opacity: 0; pointer-events: none;
  transition: opacity 140ms var(--ease);
}
.task-act:hover { opacity: .88; }

/* A row the desk is waiting on somebody else for.
   It stays on the list, because it is still the desk's to finish, but it is not
   today's work and nothing on it is asking for anything. It recedes by weight
   rather than by disappearing or going grey — a person still needs to be able to
   read the whole picture, and a waiting renewal is not a lesser renewal. */
.task.held .task-label { font-weight: 500; color: var(--ink-2); }

@media (hover: hover) {
  .task:hover .task-tag { opacity: 0; }
  .task:hover .task-act { opacity: 1; pointer-events: auto; }
  .task:focus-within .task-tag { opacity: 0; }
  .task:focus-within .task-act { opacity: 1; pointer-events: auto; }
}

/* A touch screen has no hover, so the button would have to be permanent — and a
   permanent button is not the row the page promises. There the row itself is the
   control and the actions live in the panel it opens, which is a better target
   for a thumb anyway. The tag stays: it is the row's identity. */
@media (hover: none) {
  .task-act { display: none; }
  .task { cursor: pointer; }
  .task:active { background: var(--hover); }
  .task-tag { opacity: 1; }
}

.task-tag.late { color: var(--warn); background: var(--warn-soft); }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 7px; padding: 9px 15px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--card); color: var(--ink);
  font: inherit; font-size: 13.5px; font-weight: 600; letter-spacing: -.1px;
  cursor: pointer; white-space: nowrap;
  transition: background 160ms var(--ease), border-color 160ms var(--ease), opacity 160ms var(--ease);
}
.btn:hover { background: var(--bg-2); }
.btn.primary { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.btn.primary:hover { opacity: .86; }
.btn.ghost { background: none; border-color: transparent; color: var(--ink-3); }
.btn.ghost:hover { background: var(--line-2); color: var(--ink); }
.btn.small { padding: 7px 12px; font-size: 12.5px; }
.btn:disabled { opacity: .45; cursor: default; }

/* ------------------------------------------------------------ empty */

.empty {
  padding: 40px 24px 60px;
  max-width: 460px;
  color: var(--ink);
  font-size: inherit;
  /* Clearance for the ask bar, which floats over the bottom of the column.
     The first screen is the one place that must never be obstructed: it is
     asking somebody to do something, and the bar was sitting across the button
     that does it. `.groups-host` carries the same clearance for the list. */
  padding-bottom: calc(132px + env(safe-area-inset-bottom));
}
.empty-line { font-size: 16px; font-weight: 650; letter-spacing: -.3px; margin: 0 0 8px; }
.empty-sub { font-size: 14px; line-height: 22px; color: var(--ink-3); margin: 0; }
.empty-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }

/* ------------------------------------------------------------ the account */

.account { position: relative; }

.avatar {
  width: 34px; height: 34px; border-radius: 999px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  font: inherit; font-size: 13px; font-weight: 700; letter-spacing: -.2px;
  text-transform: uppercase;
}
.avatar:hover { border-color: var(--ink-4); }
.avatar[aria-expanded="true"] { border-color: var(--ink); }

.menu {
  position: absolute; top: 44px; right: 0; z-index: 50;
  width: 304px;
  background: var(--card);
  border-radius: var(--r-2xl);
  box-shadow: 0 1px 2px rgba(10, 10, 10, .06), 0 16px 44px rgba(10, 10, 10, .16);
  overflow: hidden;
}
.menu[hidden] { display: none; }

.menu-head { padding: 18px 18px 14px; }
.menu-business { display: block; font-size: 14px; font-weight: 650; letter-spacing: -.2px; }
.menu-email { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 2px; word-break: break-all; }

.menu-body { padding: 0 8px 8px; }

.menu-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; text-align: left; padding: 9px 10px;
  border: 0; border-radius: var(--r-lg); background: none;
  font: inherit; font-size: 13.5px; color: var(--ink); cursor: pointer;
}
.menu-item:hover { background: var(--hover); }
.menu-item .k { font-size: 11px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; color: var(--ink-4); }
.menu-item .v { color: var(--ink-3); font-size: 13px; text-align: right; }
.menu-item .v.wide { flex: 1; min-width: 0; text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.menu-item .v.wide + .v { display: none; }
.menu-item .v.wide { max-width: 60%; }
.menu-item.danger { color: var(--bad); }
.menu-sep { height: 1px; background: var(--line-2); margin: 6px 10px; }

.menu-form { display: flex; gap: 6px; padding: 4px 10px 8px; }
.menu-form input {
  flex: 1; min-width: 0; padding: 8px 11px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg); color: var(--ink); font: inherit; font-size: 13px;
}
.menu-form input:focus { outline: none; border-color: var(--ink-4); }
.menu-form button {
  flex: none; padding: 8px 12px; border-radius: var(--r-lg);
  border: 1px solid var(--ink); background: var(--ink); color: var(--bg);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}

/* ------------------------------------------------------------ the chat */

.chat-head {
  display: none;
  align-items: baseline; gap: 10px;
  padding: 26px 24px 14px; flex: none;
}
.chat-head h2 { font-size: 18px; line-height: 28px; font-weight: 700; letter-spacing: -.2px; margin: 0; }
.chat-head .muted { font-size: 12px; line-height: 16px; color: var(--ink-3); }

.thread {
  flex: 1; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  /* A flex item will not shrink below its own content unless it is told to, so
     without this the thread keeps its full height, never becomes a scroll
     container, and passes the scrolling up to a page that cannot show it. */
  min-height: 0;
  padding: 20px 24px 8px;
  display: flex; flex-direction: column; gap: 4px;
}

.day {
  align-self: center; margin: 18px 0 10px;
  font-size: 11px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
  color: var(--ink-4);
}
.day:first-child { margin-top: 0; }

/* A bubble is a block that shrinks to its text, not a flex column.
   As a column flex item the box takes its width from its own content, which is
   circular, and the browser resolves it by wrapping at a single character. */
.msg { max-width: min(560px, 82%); margin-top: 6px; }
.msg.mine { margin-left: auto; text-align: right; }
.msg.theirs { margin-right: auto; text-align: left; }

.msg-body {
  display: inline-block; text-align: left; overflow-wrap: anywhere;
  padding: 11px 15px; border-radius: var(--r-2xl);
  font-size: 14.5px; line-height: 22px; letter-spacing: -.1px;
  background: var(--bg-2); color: var(--ink);
  white-space: pre-wrap; word-break: break-word;
}
[data-theme="dark"] .msg-body { background: #212121; }
.msg.mine .msg-body { background: var(--accent); color: #fff; }
.msg.mine .msg-body a { color: #fff; }

.msg-shot { max-width: 260px; border-radius: var(--r-xl); border: 1px solid var(--line); display: block; margin-left: auto; }
.msg.theirs .msg-shot { margin-left: 0; }
.msg-time { display: block; font-size: 11px; color: var(--ink-4); padding: 4px 4px 0; }

.typing {
  display: inline-flex; gap: 4px; align-self: flex-start;
  padding: 14px 16px; background: var(--bg-2); border-radius: var(--r-2xl);
}
[data-theme="dark"] .typing { background: #212121; }
.typing span { width: 5px; height: 5px; border-radius: 999px; background: var(--ink-4); animation: odBlink 1.2s infinite; }
.typing span:nth-child(2) { animation-delay: .16s; }
.typing span:nth-child(3) { animation-delay: .32s; }
@keyframes odBlink { 0%, 60%, 100% { opacity: .28; } 30% { opacity: 1; } }

/* What to say when there is nothing to say yet. */
.thread-empty { margin: auto 0; padding: 8px 0 20px; }
.thread-empty h3 { font-size: 19px; font-weight: 700; letter-spacing: -.5px; margin: 0 0 8px; }
.thread-empty p { font-size: 14px; line-height: 22px; color: var(--ink-3); margin: 0 0 20px; max-width: 420px; }
.suggestions { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.suggestion {
  text-align: left; padding: 11px 15px;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--card); color: var(--ink-2);
  font: inherit; font-size: 13.5px; cursor: pointer;
  transition: border-color 160ms var(--ease), color 160ms var(--ease);
}
.suggestion:hover { border-color: var(--ink-4); color: var(--ink); }

/* ------------------------------------------------------------ composer */

.attachments { display: flex; gap: 8px; padding: 0 24px 8px; }
.attachment { position: relative; }
.attachment img { width: 54px; height: 54px; object-fit: cover; border-radius: var(--r-lg); border: 1px solid var(--line); display: block; }
.attachment button {
  position: absolute; top: -6px; right: -6px; width: 19px; height: 19px;
  border-radius: 999px; border: 1px solid var(--line); background: var(--card); color: var(--ink-3);
  font-size: 12px; line-height: 1; cursor: pointer; padding: 0;
}

.composer {
  flex: none; display: flex; align-items: flex-end;
  padding: 10px 24px 18px;
}

/* One field.
   The attach button, the text and the two actions share a single edge and a
   single radius, so the place you type into reads as one place. Four controls
   in a row with gaps between them is a toolbar, and a toolbar makes the person
   work out which part is where the words go.
   `align-items: flex-end` so that when the text wraps to three lines the
   buttons drop to the bottom with it instead of floating in the middle. */
/* Named for the composer and nothing else. It was `.field`, which the sign-in
   page already used for its own inputs — a generic name in a stylesheet four
   surfaces share is an invitation, and the form stopped looking like a form the
   moment this rule landed. */
.composer-field {
  flex: 1; display: flex; align-items: flex-end; gap: 0;
  min-width: 0;
  border: 1px solid var(--line); border-radius: var(--r-3xl);
  background: var(--card);
  padding: 4px;
  transition: border-color 160ms var(--ease);
}
.composer-field:focus-within { border-color: var(--ink-4); }

.composer textarea {
  flex: 1; min-width: 0; resize: none;
  border: 0; background: none; color: var(--ink); font: inherit;
  font-size: 14.5px; line-height: 22px; letter-spacing: -.1px;
  padding: 11px 8px; max-height: 132px; min-height: 44px;
  outline: none;
}
.composer textarea::placeholder { color: var(--ink-4); }

/* The buttons live inside the field, so they carry no edge of their own —
   only the send does, because it is the one that does something. */
.icon-btn, .send-btn {
  flex: none; width: 44px; height: 44px; border-radius: 999px;
  display: grid; place-items: center; cursor: pointer;
  border: 0; background: none; color: var(--ink-4); padding: 0;
  transition: color 160ms var(--ease), background 160ms var(--ease), opacity 160ms var(--ease);
}
.icon-btn svg, .send-btn svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.icon-btn:hover { color: var(--ink); }
.icon-btn.live { background: var(--bad-soft); color: var(--bad); }
.send-btn { background: var(--ink); color: var(--bg); margin-left: 2px; }
.send-btn svg { fill: currentColor; stroke: none; }
.send-btn:hover { opacity: .86; }
.send-btn:disabled { opacity: .35; cursor: default; }

/* ------------------------------------------------------------ the opened row */

/* The page behind is put out of focus rather than merely darkened, so the panel
   reads as the only thing in front of the person. */
.modal {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 20px;
  background: rgba(10, 10, 10, .42);
  -webkit-backdrop-filter: blur(7px) saturate(1.15);
  backdrop-filter: blur(7px) saturate(1.15);
}
.modal[hidden] { display: none; }
.modal-inner {
  /* Stated, not merely omitted: an earlier rule for this class carried
     `position: relative` for a close button that floated inside it. Nothing here
     is positioned any more, so it is said outright. */
  position: static;
  width: 100%; max-width: 560px; max-height: 86dvh; overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-2xl);
  /* No bottom padding on the card: the action row supplies it, so that the row
     can pin itself to the bottom edge without a gap under it. */
  padding: 16px 28px 0;
  box-shadow: none;
}
/* Its own row, right-aligned, above the title. It used to float over the top
   right corner and a long account name ran underneath it. */
.modal-top {
  display: flex; justify-content: flex-end;
  margin: 0 0 6px;
}
.modal-close {
  /* Same again: an earlier rule pinned this to the corner of the card with a
     `&times;` in it. Every one of those has to be said back, or the close
     control floats over the title it was moved out of the way of. */
  position: static; top: auto; right: auto;
  flex: none; width: 36px; height: 36px; border-radius: 999px;
  display: grid; place-items: center;
  border: 1px solid var(--line); background: var(--card);
  color: var(--ink-3); font-size: 0; line-height: 1; cursor: pointer; padding: 0;
  transition: color 160ms var(--ease), border-color 160ms var(--ease);
}
.modal-close svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.modal-close:hover { color: var(--ink); border-color: var(--ink-4); }
.modal-inner h3 { font-size: 20px; font-weight: 700; letter-spacing: -.5px; line-height: 27px; margin: 0 0 8px; }
.modal-sub { font-size: 14px; line-height: 22px; color: var(--ink-3); margin: 0 0 18px; }
/* The decision stays where the thumb is.
   A row's file is longer than a phone, so the button that acts on it used to sit
   below the fold — the person read the whole thing, scrolled to the end of it,
   and only then discovered there was something to press. It pins to the bottom
   of the card and the evidence scrolls underneath it. */
.modal .row {
  position: sticky; bottom: 0; z-index: 1;
  background: var(--card);
  margin-top: 20px;
  padding: 12px 0 28px;
}

.detail-loading { font-size: 13.5px; color: var(--ink-3); padding: 14px 0; }

/* ------------------------------------------------------------------ handled

   Reassurance, not work. Behind a disclosure because nobody needs it open, and
   on the screen because "what has it been doing" is the first question a
   customer asks and a log they never open is not an answer. */

.done-wrap { margin: 4px 24px 28px; }
.done-wrap summary {
  font-size: 12.5px; font-weight: 620; color: var(--ink-3);
  cursor: pointer; list-style: none; padding: 6px 0;
}
.done-wrap summary::-webkit-details-marker { display: none; }
.done-wrap summary::after { content: " ›"; color: var(--ink-4); }
.done-wrap[open] summary::after { content: " ⌄"; }
.done-wrap summary:hover { color: var(--ink); }

.done-list { list-style: none; margin: 4px 0 0; padding: 0; }
.done-item { padding: 7px 0; }
.done-title { display: block; font-size: 13px; line-height: 18px; color: var(--ink-2); }
.done-what { display: block; font-size: 11.5px; line-height: 16px; color: var(--ink-4); margin-top: 1px; }

/* ------------------------------------------------------------- permissions

   Six verbs, each one a switch. The row has to say three things and no more:
   what the permission is, what it would take off the person's plate, and which
   way it is set. The count is the argument — a permission screen without numbers
   is a settings page, and nobody grants a permission because it sounds sensible. */

.list-sub-end { display: flex; align-items: center; gap: 6px; flex: none; }

.perm {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 0;
}
.perm-main { flex: 1; min-width: 0; }
.perm-name { display: block; font-size: 14px; line-height: 20px; font-weight: 620; letter-spacing: -.1px; }
.perm-blurb { display: block; font-size: 12.5px; line-height: 17px; color: var(--ink-3); margin-top: 2px; }
.perm-count { display: block; font-size: 12px; line-height: 16px; margin-top: 5px; font-weight: 600; }
.perm-count.on { color: var(--good); }
.perm-count.off { color: var(--ink-4); }

/* A switch, not a checkbox: the two states are two sentences, and both are
   readable without working out which way round a tick is. */
.perm-toggle {
  flex: none; width: 116px; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--card); color: var(--ink-3);
  font: inherit; font-size: 11.5px; font-weight: 620; cursor: pointer;
  transition: background 160ms var(--ease), color 160ms var(--ease), border-color 160ms var(--ease);
}
.perm-toggle:hover { color: var(--ink); border-color: var(--ink-4); }
.perm-toggle.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.perm-toggle:disabled { opacity: .55; cursor: default; }
.perm-locked { font-size: 11.5px; font-weight: 600; color: var(--ink-4); flex: none; max-width: 116px; text-align: right; }
.perm-unknown { font-size: 12.5px; line-height: 18px; color: var(--ink-3); margin: 4px 0 0; }

/* The limits, in the panel where somebody is deciding what to allow.
   Bordered rather than plain, because it is the answer to the question the
   panel is asking and it should not read as more small print. */
.perm-limits {
  margin-top: 18px; padding: 15px 16px;
  border: 1px solid var(--line); border-left: 3px solid var(--ink);
  border-radius: var(--r-lg); background: var(--bg-2, #fafafa);
}
.perm-limits-title {
  margin: 0 0 9px; font-size: 12.5px; font-weight: 680;
  letter-spacing: -.1px; color: var(--ink);
}
.perm-limits p {
  margin: 0 0 10px; font-size: 12.5px; line-height: 19px; color: var(--ink-2);
}
.perm-limits-why { color: var(--ink-3); }
.perm-limits-link {
  display: inline-block; margin-top: 2px;
  font-size: 12.5px; font-weight: 620; color: var(--accent); text-decoration: none;
}
.perm-limits-link:hover { text-decoration: underline; }

/* ------------------------------------------------------------- the desk's work

   What the desk did, on the row it did it to. Two things this must not be:
   decorated (it is a record, not a notification), and quiet about the one line
   a person actually needs — what it is waiting for, or what it needs from them. */

.held {
  display: block; margin: 0 0 18px; padding: 12px 14px;
  border-radius: var(--r-lg); background: var(--line-2);
}
.held-what { display: block; font-size: 13.5px; font-weight: 620; letter-spacing: -.1px; }
.held-when { display: block; font-size: 12.5px; line-height: 17px; color: var(--ink-3); margin-top: 3px; }

.work { margin: 0 0 18px; }
.work-when {
  float: right; font-weight: 500; color: var(--ink-4); font-size: 12px;
  letter-spacing: 0; text-transform: none;
}
.work-line { font-size: 13.5px; line-height: 20px; margin: 0 0 8px; }
.work-line:last-child { margin-bottom: 0; }
.work-label {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: var(--ink-4); margin-bottom: 2px;
}
/* The two lines a person has to act on are the ones that carry weight. */
.work-line.needs .work-label,
.work-line.needs { color: var(--bad); }
.work-line.waits .work-label { color: var(--accent); }
.work-full { margin-top: 10px; }
.work-full summary {
  font-size: 12.5px; font-weight: 600; color: var(--ink-3); cursor: pointer;
  list-style: none;
}
.work-full summary::-webkit-details-marker { display: none; }
.work-full summary:hover { color: var(--ink); }
.work-full .draft-body { margin-top: 10px; max-height: 260px; overflow-y: auto; }
[data-theme="dark"] .held { background: #1f1f1f; }

/* `1fr` is `minmax(auto, 1fr)`, and an `auto` minimum is min-content — so one
   long unbreakable value, an email address is the usual one, pushes the second
   column past the card and every fact after it is clipped. Zeroing the minimum
   is what keeps the column inside the card. */
.facts { display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 8px 16px; font-size: 13.5px; margin: 0 0 22px; }
.facts dt { color: var(--ink-3); }
.facts dd { margin: 0; font-weight: 600; letter-spacing: -.1px; min-width: 0; overflow-wrap: anywhere; }

/* Every property the earlier rule set is restated here. A later rule that only
   sets some of them leaves the rest behind, and the rest were written for the
   classic console — a pale background, monospace-ish sizing, 15px of padding. */
.draft {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  overflow: hidden;
  margin-bottom: 22px;
  padding: 0;
  white-space: normal;
  font-size: inherit;
  line-height: inherit;
}
.draft-head {
  font-size: 10.5px; font-weight: 700; letter-spacing: .9px; text-transform: uppercase;
  color: var(--ink-4); padding: 10px 16px; background: var(--bg-2); border-bottom: 1px solid var(--line);
}
[data-theme="dark"] .draft-head { background: #141414; }
.draft-subject { padding: 14px 16px 0; margin-bottom: 0; font-size: 14px; font-weight: 650; }
.draft-body {
  margin: 0; padding: 9px 16px 16px; font: inherit; font-size: 13.5px; line-height: 21px;
  white-space: pre-wrap; word-break: break-word; color: var(--ink-2); max-height: 280px; overflow-y: auto;
}

/* ------------------------------------------------------------ banner */

.banner {
  position: fixed; left: 50%; transform: translateX(-50%);
  top: 72px; z-index: 70;
  max-width: min(520px, calc(100vw - 32px));
  padding: 11px 18px; border-radius: var(--r-xl);
  background: var(--ink); color: var(--bg);
  font-size: 13.5px; font-weight: 550; line-height: 19px;
  border-bottom: 0;
  box-shadow: 0 8px 28px rgba(10, 10, 10, .18);
}
.banner[hidden] { display: none; }
.banner.error { background: var(--bad); color: #fff; }

/* ============================================================
   WIDE
   There is room for both, so both are shown. Hiding one behind
   a tab on a 27 inch display is the mobile answer applied to a
   problem that no longer exists.
   ============================================================ */

@media (min-width: 900px) {
  body.shell { padding-bottom: 0; height: 100dvh; overflow: hidden; }
  .workspace { grid-template-columns: 460px 1fr; }

  .switch { display: none; }

  /* Both panes are shown, so the inactive one still has to lay itself out.
     Without `flex-direction` here it takes the default row and puts the chat
     head, the thread and the composer side by side — the thread collapses to
     whatever the composer leaves it. */
  .screen { display: flex; flex-direction: column; }

  /* Two rounded surfaces, a gap, and nothing drawn between them. The list and
     the chat are separate places; that is said by each having its own edge,
     not by a line ruled down the middle of the screen. */
  .workspace { gap: 14px; padding: 0 14px 14px; }

  #screen-list,
  #screen-chat {
    background: var(--card);
    border-radius: var(--r-3xl);
    overflow: hidden;
  }
  #screen-list { overflow-y: auto; }

  .chat-head { display: flex; }
  .list-head { padding: 26px 24px 14px; }

  /* The two headings are read as one line across the screen, so they sit on
     one. The list has the progress bar above it and the chat has nothing, so
     the chat reserves that space rather than starting 21px higher: 22px of bar
     margin, 3px of bar, and the same 26px the list head uses. */
  .chat-head { padding: 47px 24px 14px; }
  .rows { padding-bottom: 32px; }
  .row { padding: 15px 24px 17px; }
  .thread { padding: 24px 32px 8px; }
  .composer { padding: 10px 32px 22px; }
  .attachments { padding: 0 32px 8px; }
}

@media (min-width: 1280px) {
  .workspace { grid-template-columns: 520px 1fr; }
  .thread { padding-left: max(32px, calc(50% - 380px)); padding-right: max(32px, calc(50% - 380px)); }
  .composer { padding-left: max(32px, calc(50% - 380px)); padding-right: max(32px, calc(50% - 380px)); }
  .attachments { padding-left: max(32px, calc(50% - 380px)); }
}

/* A phone in landscape has no vertical room to waste. */
@media (max-height: 520px) and (max-width: 899px) {
  .topbar { height: 50px; }
  .list-head { padding-top: 16px; }
}

/* ============================================================
   FOCUS
   Either pane can be put away so the other takes the room. A
   list of sixty is a different job from a conversation, and
   neither should be squeezed by the other when it is the work.
   ============================================================ */

.list-select {
  flex: none; padding: 4px 12px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--card); color: var(--ink-3);
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.list-select:hover { color: var(--ink); border-color: var(--ink-4); }
.list-select.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }

.pane-toggle {
  margin-left: auto; width: 28px; height: 28px; border-radius: var(--r-md);
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid transparent; background: none; color: var(--ink-4); padding: 0;
}
.pane-toggle:hover { color: var(--ink); border-color: var(--line); }
.pane-toggle svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.pane-reopen { display: none; }

/* One pane, the whole width. */
@media (min-width: 900px) {
  .workspace.only-list { grid-template-columns: 1fr; }
  .workspace.only-chat { grid-template-columns: 1fr; }
  .workspace.only-list #screen-chat { display: none; }
  .workspace.only-chat #screen-list { display: none; }

  .workspace.only-list #screen-list .pane-reopen,
  .workspace.only-chat #screen-chat .pane-reopen {
    display: inline-flex; align-items: center; gap: 7px;
    margin: 20px 24px 0; padding: 7px 13px;
    border: 1px solid var(--line); border-radius: var(--r-lg);
    background: var(--card); color: var(--ink-3);
    font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  }
  .workspace.only-list #screen-list .pane-reopen:hover,
  .workspace.only-chat #screen-chat .pane-reopen:hover { color: var(--ink); border-color: var(--ink-4); }

  /* The list runs wide — two columns of rows, so sixty is not a scroll. */
  .workspace.only-list #rows .group .rows { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; align-items: start; }
  .workspace.only-list .task { margin: 0 10px 8px; }
}

/* ============================================================
   BULK
   Sixty rows is not sixty decisions. Selection is entered
   deliberately, so at rest the list is exactly the row the
   landing page promises — no checkboxes on every card.
   ============================================================ */

/* Pinned to the top of the list while it is up.
   Selecting is a mode, and a mode has to say so wherever the person has scrolled
   to. It used to sit above the rows, so choosing something from the bottom of a
   sixty row list meant scrolling back up to find out how many were chosen and
   what could be done with them. */
.bulk {
  position: sticky; top: 0; z-index: 15;
  display: flex; flex-direction: column; gap: 8px;
  margin: 0 10px 12px; padding: 10px 12px;
  background: var(--bg-2); border-radius: var(--r-xl);
}

/* Rows disappear into the bar's edge rather than past it.
   Two things were wrong with the edge. It was rounded on all four corners, so a
   card sliding under it showed through the two bottom notches — the card looked
   like it was leaking around the bar rather than going behind it. And the
   boundary itself was a hard line through whatever glyphs happened to be there.
   So: the bottom corners are squared while the bar is pinned, which makes the
   edge one straight line the full width of the list, and a short fade in the
   bar's own colour sits under it so what emerges does so gradually.
   Only while it is actually stuck. At rest the bar is an ordinary rounded card
   under the heading, and it should look like one. */
.bulk.stuck {
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}
.bulk.stuck::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 100%;
  height: 22px;
  background: linear-gradient(to bottom, var(--bg-2), transparent);
  pointer-events: none;
}
.bulk[hidden] { display: none; }
.bulk-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.bulk-count { font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; }

/* The actions are one row, pushed right, and they wrap right as well — so a
   narrow pane stacks them against the same edge instead of leaving them
   scattered. */
.bulk-actions {
  display: flex; align-items: center; justify-content: flex-end;
  flex-wrap: wrap; gap: 8px;
}

.bulk-scope { display: flex; align-items: center; gap: 2px; }

/* Both say what they do. One of them used to be an ✕, which is a shape rather
   than an instruction, and sitting next to "Approve" it read as "cancel the
   approval" rather than "change the selection". */
.bulk-link {
  padding: 5px 9px; border: 1px solid transparent; border-radius: var(--r-md);
  background: none; color: var(--ink-3);
  font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
}
.bulk-link:hover { color: var(--ink); border-color: var(--line); }

/* The box only exists while selecting. */
.task.pick { cursor: pointer; }
.task.picked { border-color: var(--accent); background: var(--accent-soft); }
.task-box {
  width: 18px; height: 18px; border-radius: 5px;
  border: 1.5px solid var(--line); background: var(--card);
  display: grid; place-items: center; flex: none; cursor: pointer;
}
.task-box svg { width: 12px; height: 12px; fill: none; stroke: transparent; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.task.picked .task-box { background: var(--accent); border-color: var(--accent); }
.task.picked .task-box svg { stroke: #fff; }

@media (max-width: 620px) {
  .bulk .btn { font-size: 12px; padding: 6px 10px; }
}

/* Nothing to put away when the other pane is already gone. */
.workspace.only-list #screen-list .pane-toggle,
.workspace.only-chat #screen-chat .pane-toggle { display: none; }

/* ============================================================
   SIXTEEN

   iOS zooms the whole page in when a field smaller than sixteen pixels takes
   focus, and it does not zoom back out. The layout ends up wider than the
   screen and stays that way until the page is reloaded — a keyboard opening
   should not be able to do that.

   Every field on every page that loads this stylesheet is floored here. It is a
   list of names rather than `input, textarea, select`, because the rules that
   set them small are classes and an id, and a bare element selector loses to a
   class however late it appears. Naming them makes the tie and the order wins.
   ============================================================ */

@media (max-width: 899px), (hover: none) {
  .composer textarea,
  .form input,
  .form textarea,
  .search,
  .modal-input,
  .menu-form input,
  #say,
  #csv-text,
  #token,
  /* And then everything, so a control nobody thought about is covered by
     default rather than by being remembered. The named list above is what
     actually does the work — a bare element selector loses to a class however
     late it appears — and this line catches whatever has no class at all. */
  input,
  textarea,
  select { font-size: 16px; }
}

/* ============================================================
   THUMB
   A control sized to look right on a laptop is not a control a
   thumb can hit. 44px is the smallest target that does not need
   aiming.

   Keyed on the pointer as well as the width, because a tablet in
   landscape is wider than the breakpoint and still has no mouse.
   The desktop mock keeps its own sizes: a mouse is not a thumb,
   and the mock is the thing being sold.

   This lives at the end of the stylesheet on purpose. Equal
   specificity is settled by source order, so an override written
   above the rules it means to change does nothing at all — which
   is exactly what happened the first time it was written.
   ============================================================ */

@media (max-width: 899px), (hover: none) {
  #theme-toggle,
  #avatar { width: 44px; height: 44px; }

  .list-select { padding: 0 14px; min-height: 44px; }

  .pane-toggle { width: 44px; height: 44px; }
  .modal-close { width: 44px; height: 44px; }
  .icon-btn,
  .send-btn { width: 44px; height: 44px; }
  .bulk-link { min-height: 44px; padding: 0 12px; }

  /* The panel's own actions, stacked the way a thumb reads them: the one that
     does the work across the full width, then the two that only change your mind
     side by side beneath it. Three buttons in a row on a phone gave each of them
     a third of the width and left them uneven lengths. */
  .modal .row {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  }
  .modal .row .btn { width: 100%; min-height: 44px; justify-content: center; }
  .modal .row .btn.primary { grid-column: 1 / -1; }
  .modal .row { padding: 12px 0 22px; }
}

@media (max-width: 899px) {
  .switch { padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); }
  .switch-item { min-height: 46px; padding: 0; }

  /* There is no bottom bar any more — the switcher went with the second pane.
     What is left is the phone's own home indicator, which the floating ask bar
     and the composer both sit above. Reserving for a bar that no longer exists
     was 63px of dead space at the bottom of every screen on a phone. */
  body.shell { padding-bottom: env(safe-area-inset-bottom); }

  /* The desk is bounded to the viewport now, so each screen scrolls inside
     itself rather than the two of them sharing one page scroll. */
  #screen-list { overflow-y: auto; -webkit-overflow-scrolling: touch; }

  /* Stated rather than inherited. This used to come from the console's bare
     `main` rule — the same fourteen pixels, arrived at by accident on a
     surface that rule was never written for. */
  .workspace { padding: 0 14px; }

  /* The card is centred in the viewport, so the bar sits over its last line.
     Reserving the bar's height at the bottom moves the card clear of it. */
  .modal { padding: 16px 16px calc(16px + 63px + env(safe-area-inset-bottom)); }
  .modal-inner { max-height: calc(100dvh - 32px - 63px - env(safe-area-inset-bottom)); padding: 12px 20px 0; }
}

/* ============================================================
   ONE COLUMN

   The desk used to be two panes with a toggle to put either
   away. A list beside a detail pane is the mail-client shape,
   and that shape makes a promise — pick on the left, read on
   the right — that these two things could not keep. One is a
   record of work, the other is a conversation; they are never
   looked at together, so showing them together was pure cost.

   What is here instead: one column, a comfortable measure, and
   three states of which only one is ever on screen. The chat
   became a mode. The item detail expands where the item is
   rather than in a box over the top of everything.

   A note on the measure: this is a reading surface now, not a
   control panel. Nothing on it is wider than it needs to be,
   and the whole thing sits in the middle of whatever screen it
   is given instead of stretching to fill it.
   ============================================================ */

.column {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  width: 100%;
  max-width: var(--measure);
  margin: 0 auto;
  /* Enough room under the last group for the floating ask bar to clear it.
     The bar sits 18px off the bottom and is about 46px tall; 96px was short
     once the handled group grew, and the last row ended up behind it. */
  padding: 0 var(--gutter) calc(120px + env(safe-area-inset-bottom));
  overflow-y: auto;
  overscroll-behavior: contain;
}

.view { display: block; min-height: 0; }
.view[hidden] { display: none; }
/* The ask view fills what is left of the column rather than a percentage of it.
   `height: 100%` looked right and did nothing useful — the composer ended up
   floating part way down the screen with empty space underneath, which is a
   conversation that cannot use the room it has.

   And the column reserves 96px at the bottom for the ask bar. In here there is
   no ask bar, so it is dead space under the composer. */
.view.ask { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-bottom: 0; }
.column.asking { padding-bottom: 0; }
.view.ask .composer { padding-bottom: 22px; }
/* Equal specificity, so the hidden state has to be stated after it — otherwise
   the ask view sits on top of the desk and the desk looks broken. */
.view.ask[hidden] { display: none; }

/* ---------------------------------------------------------- the verdict */

/* The headline is the verdict, not the inventory. It is the first thing read
   and, on a good day, the only thing that has to be. */
.verdict { padding: 34px 0 20px; }
.verdict h1 {
  font-size: 30px; line-height: 36px; font-weight: 680; letter-spacing: -1px;
  margin: 0; max-width: 22ch;
}
.verdict-sub {
  font-size: 14px; line-height: 22px; color: var(--ink-3);
  margin: 8px 0 0; max-width: 52ch;
}
.verdict-actions { display: flex; align-items: center; gap: 8px; margin-top: 18px; flex-wrap: wrap; }

/* Narrowed to one arrangement, the heading is a name rather than a verdict.
   A name set at verdict size wraps onto two lines and shouts, and the thing you
   are looking at is a card thirty pixels below it saying the same words. */
.verdict.narrowed { padding-top: 26px; }
.verdict.narrowed h1 {
  font-size: 21px; line-height: 28px; letter-spacing: -.4px; font-weight: 660;
  max-width: 40ch;
}
/* The meter measures the whole desk, so under one arrangement it is measuring
   something other than what the screen is about. */
.verdict.narrowed .meter { display: none; }

/* The meter and its sentence, read as one thing. The bar is 3px and says
   nothing on its own; the line under it is what makes it mean something. */
.meter { margin-top: 18px; max-width: 420px; }
.meter .progress { margin: 0; }
.meter-note {
  font-size: 12.5px; line-height: 18px; color: var(--ink-3);
  margin: 8px 0 0; font-variant-numeric: tabular-nums;
}
.meter-note strong { font-weight: 660; color: var(--ink-2); }

.vbtn {
  padding: 8px 14px; min-height: 44px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--ink-2);
  font: inherit; font-size: 12.5px; font-weight: 620; cursor: pointer;
  transition: color 160ms var(--ease), border-color 160ms var(--ease);
}
.vbtn:hover { color: var(--ink); border-color: var(--ink-4); }
.vbtn.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }

/* ------------------------------------------------- the groups, and the rows */

/* The clearance for the floating ask bar goes on the content, not on the
   scroller: padding on a scroll container does not always count toward what it
   can scroll, and the last group stayed behind the bar. */
/* Clearance for the floating ask bar, so the last group is not behind it.
   Only when there is something in it: an empty host still carried the full 120px,
   which on a brand-new desk pushed the first-run form down the page and under the
   bar — the one screen that is asking somebody to do something, with the button
   they need to press sitting underneath a floating control. */
.groups-host { padding-bottom: calc(120px + env(safe-area-inset-bottom)); }
.groups-host:empty { padding-bottom: 0; }

/* ------------------------------------------------------------- the lives */

/*
  The arrangements, above the work.

  These sit higher on the screen and are quieter than the rows below them, which
  is deliberate: they are not work, they are the thing the work is for. A card
  per arrangement would make them the loudest object on a screen whose job is to
  say what needs doing, so each is a row like any other — same measure, same
  radius, same border — with one difference that earns its place: a meter, so
  "how is it going" is answered without reading a number.
*/
.lives { display: block; margin-bottom: 4px; }
.lives[hidden] { display: none; }

.lives-head {
  display: flex; align-items: baseline; gap: 12px;
  padding: 14px 10px 8px;
}
.lives-label {
  font-size: 11.5px; font-weight: 660; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-4);
}
.lives-clear {
  margin-left: auto; padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent);
}
.lives-clear:hover { text-decoration: underline; }

/* The way out of a narrowed list, shaped like the way out of everywhere else in
   this app: an arrow and a name, where the back control always is. It replaces
   the heading rather than sitting beside it, because at that point the heading
   is not information — you already know which arrangement you pressed. */
.lives-back {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 14px 10px 0; padding: 6px 10px 6px 6px;
  border: 0; border-radius: var(--r-lg); background: none; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 620; color: var(--ink-2);
  transition: background 150ms var(--ease), color 150ms var(--ease);
}
.lives-back:hover { background: var(--hover); color: var(--ink); }
.lives-back svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.life {
  display: block; width: 100%; text-align: left; font: inherit; color: inherit;
  padding: 13px 14px; margin: 0 10px 8px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--card);
  transition: border-color 160ms var(--ease);
}
.life:hover { border-color: var(--ink-4); }
.life[aria-current="true"] { border-color: var(--accent); }

.life-top { display: flex; align-items: baseline; gap: 10px; }
.life-title {
  font-size: 14px; line-height: 20px; font-weight: 660;
  letter-spacing: -.1px; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.life-stage {
  flex: none; font-size: 11.5px; line-height: 16px; font-weight: 660;
  letter-spacing: .01em; text-transform: lowercase;
  color: var(--ink-4);
}
.life.behind .life-stage { color: var(--warn); }

.life-sub {
  display: block; margin-top: 2px;
  font-size: 12.5px; line-height: 17px; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.life.behind .life-sub { color: var(--ink); }

.life-meter {
  display: block; height: 3px; border-radius: 999px;
  background: var(--line-2); margin: 9px 0 0; overflow: hidden;
}
.life-meter > span {
  display: block; height: 100%; border-radius: 999px;
  background: var(--good);
  transition: width 320ms var(--ease);
}
.life.behind .life-meter > span { background: var(--warn); }

.life-foot {
  display: block; margin-top: 7px;
  font-size: 12px; line-height: 16px; color: var(--ink-3);
}

/* The arrangements that are fine, as one line rather than five rows. A person
   needs to be told the rest are running; they should not have to count them. */
.lives-rest {
  margin: 2px 10px 6px;
  font-size: 12.5px; line-height: 18px; color: var(--ink-3);
}
.lives-n { font-weight: 660; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.lives-n::after { content: ''; }

/* What arrived and is waiting on a decision. Not generated by reading a
   business, so it can never appear in the queue below — it has to have its own
   place or it does not exist. */
.inbound-grp .grp-head.static { cursor: default; }
.inbound-grp .grp-name { color: var(--ink); }
/* An arrival is the same card with one thing missing: there is no work on it
   yet, because who pays for it is the open question. So it is the row without
   the mark that says "done", at the same measure and radius as everything else
   rather than in a shape of its own. */
/* Still a grid, still one row: the columns are the mark and the text, and the
   mark is simply not rendered. Switching the display here would have taken the
   open button out of the grid and collapsed the target a thumb has to hit. */
.task.inbound { grid-template-columns: 1fr; cursor: pointer; }
.task.inbound .task-open { cursor: pointer; }

/* Who pays, as four answers rather than a text field. The question is the same
   every time and so are the answers, which makes it a set of buttons — a person
   should be choosing, not composing. */
.ia-body {
  margin: 0 0 16px; padding: 12px 14px;
  background: var(--line-2); border-radius: var(--r-lg);
  font-size: 13px; line-height: 19px; color: var(--ink-2); white-space: pre-wrap;
}
.ia-answers { display: grid; gap: 8px; }
.ia-answer {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--card); font: inherit; color: inherit;
  transition: border-color 140ms var(--ease), background 140ms var(--ease);
}
.ia-answer:hover { border-color: var(--accent); background: var(--accent-soft); }
.ia-answer:disabled { opacity: .5; cursor: default; }
.ia-label { display: block; font-size: 14px; line-height: 20px; font-weight: 640; letter-spacing: -.1px; }
.ia-hint { display: block; margin-top: 2px; font-size: 12.5px; line-height: 17px; color: var(--ink-3); }

.grp { margin-bottom: 6px; }
.grp-head {
  width: 100%; display: flex; align-items: baseline; gap: 10px;
  padding: 16px 0 10px; border: 0; background: none; cursor: pointer;
  font: inherit; text-align: left; color: inherit;
}
.grp-name { font-size: 13.5px; font-weight: 660; letter-spacing: -.1px; }
.grp-note { font-size: 12.5px; line-height: 18px; color: var(--ink-3); flex: 1; min-width: 0; }

/* A slot a business was told it has, before anything is in it.
   Reads as an explanation rather than as an error, because an empty Roster is
   not a fault — it is the desk saying where the artists go. */
.grp-empty {
  margin: 0; padding: 14px 16px;
  font-size: 13px; line-height: 19px; color: var(--ink-2);
}
.grp-empty-fields {
  margin: 0; padding: 0 16px 15px;
  font-size: 12px; line-height: 18px; color: var(--ink-4);
}
.grp-count {
  font-size: 12.5px; font-weight: 660; color: var(--ink-3);
  font-variant-numeric: tabular-nums; flex: none;
}
.grp-chev { flex: none; color: var(--ink-4); display: grid; place-items: center; }
.grp-chev svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; transition: transform 200ms var(--ease); }
.grp.open .grp-chev svg { transform: rotate(90deg); }
.grp-head:hover .grp-name { color: var(--ink); }

.rowlist { list-style: none; margin: 0; padding: 0; }
.rowlist[hidden] { display: none; }

/* The item, opened, in place. No box over the top of the screen: the thing you
   opened stays where it was and the page grows around it. */
.focus {
  margin: 2px 0 14px;
  padding: 18px 20px;
  border: 1px solid var(--line); border-radius: var(--r-2xl);
  background: var(--card);
}
.focus-close {
  float: right; margin: -4px -6px 0 12px;
  width: 32px; height: 32px; border-radius: 999px;
  display: grid; place-items: center;
  border: 0; background: none; color: var(--ink-4); cursor: pointer; padding: 0;
}
.focus-close:hover { color: var(--ink); }
.focus-close svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; }

/* ------------------------------------------------------------- the ask bar */

/* The way in to the chat, always in reach, never taking a pane. */
.ask-bar {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(18px + env(safe-area-inset-bottom));
  z-index: 30;
  display: flex; align-items: center; gap: 9px;
  padding: 11px 18px; min-height: 46px;
  border: 1px solid var(--line); border-radius: 999px;
  background: color-mix(in srgb, var(--card) 88%, transparent);
  -webkit-backdrop-filter: saturate(1.6) blur(14px);
  backdrop-filter: saturate(1.6) blur(14px);
  color: var(--ink-2); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  box-shadow: 0 8px 28px -14px rgba(10, 10, 10, .45);
  transition: color 160ms var(--ease), border-color 160ms var(--ease);
}
.ask-bar:hover { color: var(--ink); border-color: var(--ink-4); }
.ask-bar svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.ask-bar[hidden] { display: none; }

/* ------------------------------------------------------------ the ask view */

/* The way out, at the top left where a way out goes. It reads as a back
   control rather than a button because that is what it is — the same gesture
   people already have in their hands from every other screen they use. */
.ask-head { display: flex; align-items: center; padding: 18px 0 10px; flex: none; }

.ask-back {
  display: inline-flex; align-items: center; gap: 6px;
  margin-left: -10px; padding: 8px 12px 8px 8px; min-height: 40px;
  border: 0; border-radius: 999px; background: none;
  color: var(--ink-3); font: inherit; font-size: 13px; font-weight: 620; cursor: pointer;
  transition: color 160ms var(--ease), background 160ms var(--ease);
}
.ask-back:hover { color: var(--ink); background: var(--hover); }
.ask-back svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ============================================================
   THE MINIMUM THAT KEEPS A ROW IN ITS COLUMN

   A grid column defaults to a minimum of its own content, so one
   long line inside a row pushes the row wider than the thing it
   is in — which is what made the loss-run cards look wrong: the
   middle column could not shrink and the tag was shoved out of
   the card. The rule that used to prevent this named `.workspace`
   and `.screen`, and both of those are gone.
   ============================================================ */

.column > *,
.grp,
.grp > *,
.rowlist,
.rowlist > *,
.task > * { min-width: 0; }

/* ---------------------------------------------------------------- filters */

.filters { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 18px; }
.filters[hidden] { display: none; }

.filter {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; min-height: 38px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--ink-3);
  font: inherit; font-size: 12.5px; font-weight: 620; cursor: pointer;
  transition: color 160ms var(--ease), border-color 160ms var(--ease),
    background 160ms var(--ease);
}
.filter:hover { color: var(--ink); border-color: var(--ink-4); }
.filter.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.filter-n { font-variant-numeric: tabular-nums; font-weight: 700; }
/* The one that is actually the person's, when there is one. */
.filter.loud { border-color: var(--accent); color: var(--accent); }
.filter.loud.on { background: var(--accent); border-color: var(--accent); color: #fff; }

@media (max-width: 899px), (hover: none) {
  .filter { min-height: 44px; }
}

/* A handled row is the same card, quieter: it is a record, not a task. */
.task.done { cursor: default; }
.task.done .task-label { font-weight: 520; color: var(--ink-2); }
.task.done .task-tag { opacity: 1; }
.done-grp .grp-note { color: var(--ink-4); }

/* ------------------------------------------------------------- connected tools

   The systems the desk can reach.

   A row per system, and the row is doing one job: saying how the desk gets in.
   That is the only decision on this screen, so it is the only thing with weight
   on it — the marks are small and desaturated for the same reason the permission
   switches are plain. A catalogue that looks like a shop invites browsing, and
   browsing a tool list is not the work. */

.tt-cov { display: grid; gap: 8px; padding: 0 0 18px; }
.tt-cov-n { font-size: 26px; font-weight: 700; letter-spacing: -.7px; line-height: 32px; }
.tt-cov-n span { font-size: 14px; font-weight: 500; color: var(--ink-4); letter-spacing: 0; }
.tt-cov-note { margin: 0; font-size: 13px; line-height: 19px; color: var(--ink-3); }
.tt-cov-note strong { color: var(--ink-2); font-weight: 660; }
.tt-bar { height: 4px; border-radius: 999px; background: var(--line-2); overflow: hidden; }
.tt-bar > span { display: block; height: 100%; background: var(--ink-3); border-radius: 999px; transition: width 320ms var(--ease); }

/* The single most valuable thing to connect, said as a recommendation rather
   than as a gap. A number that only ever says what is missing is a nag. */
.tt-suggest {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 14px;
  align-items: center;
  padding: 14px 16px; margin: 0 0 20px;
  border: 1px solid var(--accent); border-radius: var(--r-2xl);
  background: var(--accent-soft);
}
.tt-suggest-k {
  grid-column: 1 / 2;
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent);
}
.tt-suggest-t { grid-column: 1 / 2; font-size: 15px; font-weight: 660; letter-spacing: -.2px; }
.tt-suggest-w { grid-column: 1 / 2; font-size: 12.5px; line-height: 17px; color: var(--ink-3); }
.tt-suggest .tt-act { grid-column: 2 / 3; grid-row: 1 / 4; }

.tt-head {
  margin: 22px 0 2px; font-size: 11.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-4);
}
.tt-list { list-style: none; margin: 0; padding: 0; }
.tt {
  display: flex; align-items: center; gap: 13px;
  padding: 12px 0; border-bottom: 1px solid var(--line-2);
}
.tt:last-child { border-bottom: 0; }
.tt.on .tt-name { color: var(--ink); }

.tt-mark {
  flex: none; width: 30px; height: 30px; border-radius: var(--r-lg);
  display: grid; place-items: center; background: var(--line-2);
  font-size: 13px; font-weight: 700; color: var(--ink-3);
  overflow: hidden;
}
.tt-mark svg { width: 30px; height: 30px; display: block; }

.tt-main { flex: 1; min-width: 0; }
/* What the row *is*. It rendered as nothing at all until the data layer was
   fixed — the panel read `name` and the row carried `display_name`, and a
   template does not complain about `undefined`. */
.tt-name { display: block; font-size: 14px; font-weight: 640; letter-spacing: -.1px; }
.tt-own {
  margin-left: 7px; padding: 1px 6px; border-radius: 999px;
  background: var(--line-2); color: var(--ink-3);
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  vertical-align: 1px;
}
.tt-blurb { display: block; font-size: 12.5px; line-height: 17px; color: var(--ink-3); margin-top: 1px; }
.tt-acct { display: block; font-size: 12px; line-height: 16px; color: var(--good); margin-top: 3px; font-weight: 600; }
.tt-acct.err { color: var(--bad); }

.tt-end { flex: none; }
.tt-state { font-size: 12px; color: var(--ink-4); }

.tt-act {
  font: inherit; font-size: 12px; font-weight: 660; line-height: 16px;
  padding: 7px 13px; border-radius: var(--r-lg); white-space: nowrap; cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  transition: border-color 150ms var(--ease), background 150ms var(--ease), color 150ms var(--ease);
}
.tt-act:hover { border-color: var(--ink-4); }
.tt-act:disabled { opacity: .5; cursor: default; }
.tt-act.quiet { border-color: transparent; color: var(--ink-3); }
.tt-act.quiet:hover { color: var(--ink); border-color: var(--line); }

.tt-note { font-size: 12.5px; line-height: 18px; color: var(--ink-3); margin: 10px 0 0; }
.tt-hint { font-size: 12px; line-height: 17px; color: var(--ink-4); margin: 10px 0 0; }

.tt-add { padding: 22px 0 4px; border-top: 1px solid var(--line); margin-top: 22px; }
.tt-add .tt-head { margin-top: 0; }
.tt-add .tt-act { margin-top: 10px; }
.tt-form { display: grid; gap: 8px; margin-top: 12px; }
.tt-form-row { display: flex; gap: 8px; align-items: center; }
.tt-form-row .tt-act { margin-top: 0; }
.tt-input, .tt-select {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  font: inherit; font-size: 13.5px;
  background: var(--bg); color: var(--ink);
}
.tt-select { flex: 1; }
.tt-input:focus, .tt-select:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* Asking for the one thing: the key. One field, because a form that asks for
   four things to reach a system is a form nobody finishes. */
/* The panel you are in the middle of, as a card above everything.
   It used to be a strip appended after the list, which put it forty screens
   below the fold — pressing "Paste a key" did open a form, and from where the
   person was sitting nothing happened at all. */
.tt-ask {
  margin: 4px 0 22px;
  padding: 18px 16px 16px;
  border: 1px solid var(--accent);
  border-radius: var(--r-2xl);
  background: var(--accent-soft);
}
.tt-ask .tt-head { margin-top: 0; color: var(--accent); }
.tt-ask .tt-input { margin-bottom: 8px; background: var(--card); }
.tt-ask .tt-form-row { margin-top: 10px; }
.tt-ask .tt-form-row .tt-act { margin-top: 0; }

@media (max-width: 599px) {
  .tt-suggest { grid-template-columns: 1fr; }
  .tt-suggest .tt-act { grid-column: 1 / 2; grid-row: auto; justify-self: start; margin-top: 8px; }
  .tt { gap: 11px; }
}

/* ------------------------------------------------- an arrangement, opened

   The same panel an obligation opens in, because they are the same kind of
   thing at different heights. What is different is the subject: a piece of work
   has one decision on it, and an arrangement has a state, a schedule and a
   history, and a person needs to be able to see all three at once before
   deciding anything about it. */

.stage-now {
  display: flex; align-items: baseline; gap: 10px;
  padding: 12px 14px; margin: 0 0 18px;
  border-radius: var(--r-lg); background: var(--line-2);
}
.stage-label {
  font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-4);
}
.stage-value { font-size: 14px; font-weight: 660; letter-spacing: -.1px; }
/* The control sits with the value it changes, at the weight of a correction
   rather than a decision. */
.stage-move {
  margin-left: auto; padding: 4px 10px; cursor: pointer;
  border: 1px solid transparent; border-radius: var(--r-md); background: none;
  font: inherit; font-size: 12px; font-weight: 620; color: var(--ink-3);
  transition: color 150ms var(--ease), border-color 150ms var(--ease);
}
.stage-move:hover { color: var(--ink); border-color: var(--line); }
.stage-pick {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: -10px 0 20px; padding: 0 2px;
}
.stage-pick[hidden] { display: none; }

.life-block { margin: 0 0 20px; }
.life-block-head {
  margin: 0 0 8px; font-size: 11.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-4);
}

.life-list { list-style: none; margin: 0; padding: 0; }
.life-item { padding: 9px 0; border-bottom: 1px solid var(--line-2); }
.life-item:last-child { border-bottom: 0; }
.life-item-what { display: block; font-size: 13.5px; line-height: 19px; font-weight: 600; letter-spacing: -.1px; }
.life-item-when { display: block; margin-top: 2px; font-size: 12px; line-height: 16px; color: var(--ink-3); }
.life-item.settled .life-item-what { font-weight: 500; color: var(--ink-3); }
.life-more { margin: 8px 0 0; font-size: 12px; color: var(--ink-4); }

/* A promise, and the schedule behind it.
   Two facts live here and they are not the same fact: what was agreed, and what
   has happened. Showing a balance alone would hide which instalment slipped, and
   which instalment slipping is the whole reason a person opened this. */
.promise-line {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 0 0 10px;
}
.promise-behind {
  font-size: 13px; font-weight: 680; color: var(--warn);
  font-variant-numeric: tabular-nums;
}
.promise-rest { font-size: 12.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

.settle { list-style: none; margin: 0; padding: 0; }
.settle-row {
  display: grid; grid-template-columns: 64px 88px 1fr auto;
  align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--r-md);
  font-size: 12.5px; font-variant-numeric: tabular-nums;
}
.settle-row:nth-child(odd) { background: var(--line-2); }
.settle-when { color: var(--ink-3); }
.settle-amount { font-weight: 620; }
.settle-state { color: var(--ink-3); }
.settle-row.paid .settle-state { color: var(--good); }
.settle-row.late .settle-state { color: var(--warn); font-weight: 620; }
.settle-row.late .settle-amount { color: var(--warn); }
.settle-row.part .settle-state { color: var(--ink-2); }

.settle-act {
  font: inherit; font-size: 11.5px; font-weight: 660; line-height: 15px;
  padding: 5px 10px; border-radius: var(--r-md); cursor: pointer;
  border: 1px solid var(--line); background: var(--card); color: var(--ink-3);
  transition: color 150ms var(--ease), border-color 150ms var(--ease);
}
.settle-act:hover { color: var(--ink); border-color: var(--ink-4); }
.settle-act:disabled { opacity: .5; cursor: default; }

.btn.quiet { background: none; border-color: transparent; color: var(--ink-3); }
.btn.quiet:hover { color: var(--ink); border-color: var(--line); }

@media (max-width: 599px) {
  .settle-row { grid-template-columns: 58px 1fr auto; row-gap: 2px; }
  .settle-state { grid-column: 1 / 3; }
  .settle-act { grid-row: 1 / 3; grid-column: 3 / 4; align-self: center; }
}

/* Recording a payment, in the row it belongs to.
   The amount arrives pre-filled with what is owed, because that is what turns up
   almost every time and a blank field would make the common case work for it. */
.settle-entry { display: inline-flex; align-items: center; gap: 6px; }
.settle-input {
  width: 84px; padding: 5px 8px; text-align: right;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--card); color: var(--ink);
  font: inherit; font-size: 12.5px; font-variant-numeric: tabular-nums;
}
.settle-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.settle-act.go { border-color: var(--accent); background: var(--accent); color: #fff; }

/* ------------------------------------------------- everything about one thing

   The merged record. Four kinds of entry in one list — what the desk sent, what
   a person wrote, what arrived from outside, what a person decided — and the
   only thing that makes the merge useful rather than misleading is that the
   first thing on every line says who it came from.

   So the left edge carries that: a hairline in a different colour per source,
   which reads at a glance down the column without needing to read a word. */

.log { list-style: none; margin: 0; padding: 0; }
.log-row {
  padding: 10px 0 10px 12px;
  border-left: 2px solid var(--line);
  margin-left: 2px;
  border-bottom: 1px solid var(--line-2);
}
.log-row:last-child { border-bottom: 0; }
/* The desk doing its own work under the mandate. */
.log-row.desk { border-left-color: var(--good); }
/* A person. The rarest entry and the most valuable one. */
.log-row.human { border-left-color: var(--accent); }
/* It came from outside — the driver, the carrier, the authority. */
.log-row.them { border-left-color: var(--ink-4); }

.log-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.log-who { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.log-row.desk .log-who { color: var(--good); }
.log-row.human .log-who { color: var(--accent); }
.log-row.them .log-who { color: var(--ink-4); }
.log-channel { font-size: 11.5px; color: var(--ink-4); }
.log-when { margin-left: auto; font-size: 11.5px; color: var(--ink-4); white-space: nowrap; }

/* A flag, not a badge: it appears only when the entry is asking for something. */
.log-flag {
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: 2px 6px; border-radius: var(--r-sm);
  background: var(--warn-soft); color: var(--warn);
}

.log-subject {
  font-size: 13px; line-height: 18px; font-weight: 620;
  letter-spacing: -.1px; margin-top: 4px;
}
.log-body {
  margin: 5px 0 0; font: inherit; font-size: 12.5px; line-height: 18px;
  color: var(--ink-2); white-space: pre-wrap; word-break: break-word;
}
.log-about { margin-top: 5px; font-size: 11.5px; color: var(--ink-4); }

/* Writing a note into the record. Big enough to think in, and it takes the
   return key — with shift-return for a new line — because a note is one thought
   and pressing Enter should file it. */
.note-box { display: grid; gap: 8px; }
.note-box textarea {
  width: 100%; padding: 10px 12px; resize: vertical; min-height: 58px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg); color: var(--ink);
  font: inherit; font-size: 13.5px; line-height: 20px;
}
.note-box textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.note-row { display: flex; align-items: center; gap: 10px; }
.note-kind { flex: 1; }
.note-kind select {
  width: 100%; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg); color: var(--ink); font: inherit; font-size: 13px;
}
.note-kind select:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* Dates on the things an arrangement is about.
   Read as a small table because that is what it is: what, when, how long. The
   colour is the whole point — amber means it is inside the lead time the desk
   watches, red means it has already gone. */
.watched { list-style: none; margin: 0; padding: 0; }
.watched-row {
  display: grid; grid-template-columns: 1fr auto auto;
  gap: 10px; align-items: baseline;
  padding: 9px 10px; border-radius: var(--r-md);
  font-size: 13px; font-variant-numeric: tabular-nums;
}
.watched-row:nth-child(4n+1) { background: var(--line-2); }
.watched-what { font-weight: 620; letter-spacing: -.1px; }
.watched-when { color: var(--ink-3); }
.watched-left { color: var(--ink-4); min-width: 84px; text-align: right; }
.watched-row.due .watched-left { color: var(--warn); font-weight: 660; }
.watched-row.past .watched-left,
.watched-row.past .watched-what { color: var(--bad); font-weight: 660; }

/* What the desk will do about it, said once underneath the date it belongs to. */
.watched-do {
  padding: 0 10px 9px; font-size: 12px; line-height: 17px;
  color: var(--ink-3); list-style: none;
}

/* ------------------------------------------------------- the first screen

   The only screen a new customer is guaranteed to see, and it has three jobs:
   ask when it has to, keep saying it is working while it works, and report what
   it found when it finds nothing. It used to do none of them — two lines of
   text over an empty list, identical whether the desk was mid-read or had never
   been given anything to read at all. */

.firstform { display: grid; gap: 14px; margin: 20px 0 0; max-width: 420px; }

/* The same shape as every other form in the product: a label above its input,
   so what the field wants is still on screen while somebody is typing the
   answer. These two fields had no labels at all and looked identical without
   them — the only clue was placeholder text, which is gone by the second
   keystroke. */
.firstform-field { display: block; }
.firstform-field label {
  display: block; font-size: 13px; font-weight: 620;
  letter-spacing: -.1px; margin-bottom: 6px;
}
.firstform-opt { font-weight: 500; color: var(--ink-4); }
.firstform input {
  width: 100%; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--card); color: var(--ink);
  font: inherit; font-size: 14px;
}
.firstform input::placeholder { color: var(--ink-4); }
.firstform input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.firstform .btn { justify-content: center; }

.empty-hint {
  margin: 10px 0 0; font-size: 12.5px; line-height: 18px;
  color: var(--bad); max-width: 420px; min-height: 18px;
}

/* A bar that moves, because the wait is real and a still screen during a real
   wait is read as a broken one. */
.building {
  margin: 20px 0 0; height: 3px; max-width: 420px;
  border-radius: 999px; background: var(--line-2); overflow: hidden;
}
.building > span {
  display: block; height: 100%; width: 34%;
  border-radius: 999px; background: var(--ink-3);
  animation: building 1.5s var(--ease) infinite;
}
@keyframes building {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(320%); }
}
@media (prefers-reduced-motion: reduce) {
  .building > span { animation: none; width: 100%; }
}

/* ---------------------------------------------------------------- billing

   The revenue screen. It is shaped like the permission panel and the tools
   panel on purpose — a customer has already learned "a modal in this app is a
   list of things with one action each", and a checkout that looks like nothing
   else in the product reads as somebody else's page. */

.bill-now {
  display: grid; gap: 3px;
  padding: 14px 16px; margin: 0 0 20px;
  border-radius: var(--r-2xl); background: var(--line-2);
}
.bill-plan { font-size: 15px; font-weight: 680; letter-spacing: -.2px; }
.bill-note { font-size: 12.5px; line-height: 18px; color: var(--ink-3); }

.plans { list-style: none; margin: 0; padding: 0; }
.plan {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 0; border-bottom: 1px solid var(--line-2);
}
.plan:last-child { border-bottom: 0; }
.plan.on .plan-name { color: var(--good); }

.plan-main { flex: 1; min-width: 0; }
.plan-name { display: block; font-size: 14px; line-height: 20px; font-weight: 640; letter-spacing: -.1px; }
.plan-blurb { display: block; font-size: 12.5px; line-height: 17px; color: var(--ink-3); margin-top: 1px; }
.plan-units { display: block; font-size: 12px; line-height: 16px; color: var(--ink-4); margin-top: 3px; }

.plan-end { flex: none; display: flex; align-items: center; gap: 12px; }
/* Tabular figures, because a column of prices that does not line up is the
   first thing anybody notices about a pricing table. */
.plan-price { font-size: 15px; font-weight: 680; font-variant-numeric: tabular-nums; white-space: nowrap; }
.plan-price > span { font-size: 11.5px; font-weight: 500; color: var(--ink-4); }
.plan-cur {
  font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--good);
}

@media (max-width: 599px) {
  .plan { flex-wrap: wrap; gap: 10px; }
  .plan-end { width: 100%; justify-content: space-between; }
}

/* ------------------------------------------------- the tools panel, three fixes

   All three came from using it rather than reading it.

   1. The header scrolled away. "Connected tools" and the line under it are the
      only things on the screen that say what you are looking at, and there are
      twenty-three rows below them. So the card stops scrolling and the list
      inside it does instead — the title is simply always there.

   2. The panel you opened used to be appended after the list, forty screens
      below the fold. It now renders above everything, and reads as the thing you
      are in the middle of rather than another row.

   3. A tool with no name rendered its blurb alone, which is what a template does
      with `undefined` — quietly. That is fixed in the data, and the styles here
      give the name the weight it needs now that it is actually there. */

#tools .modal-inner {
  display: flex;
  flex-direction: column;
  /* The card is the frame; the list inside it is what moves. */
  overflow: hidden;
}
#tools .modal-top { order: 0; }
#tools #t-title { order: 1; }
#tools #t-sub { order: 2; }
#tools #t-body {
  order: 3;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Room at the bottom so the last row is not jammed against the edge. */
  padding-bottom: 8px;
}
/* A hairline under the header once there is something to scroll under it. */
#tools #t-sub { padding-bottom: 14px; border-bottom: 1px solid var(--line-2); margin-bottom: 4px; }

/* A voice note, as a voice note.
   It rendered as an <img> with a data URL, which is a broken picture icon where
   somebody's own voice should be — and no way to check what you said before it
   goes. It can be listened to here, in the thread, and in the composer before
   sending. */
/* A real width, because `width: 100%` resolves against a bubble that has
   already shrunk to fit nothing — the player came out 50px wide, which is a
   control you cannot see the length of and can barely press. */
.msg-audio {
  display: block;
  width: 260px;
  min-width: 200px;
  max-width: 100%;
  height: 40px;
  margin-left: auto;
}
.msg.theirs .msg-audio { margin-left: 0; }

.attachment.audio { display: block; padding: 8px 10px; }
.attachment.audio audio { display: block; width: 100%; height: 34px; }

/* ------------------------------------------------------- what is in the way

   One line, above the work, naming the single next thing. Not a checklist: a
   list of five things somebody has not done is a reason to close the tab, and
   there is only ever one thing that is actually blocking value. */

.setup {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 0 10px 18px;
  padding: 14px 16px;
  border: 1px solid var(--accent);
  border-radius: var(--r-2xl);
  background: var(--accent-soft);
}
.setup[hidden] { display: none; }
.setup-main { flex: 1; min-width: 0; }
.setup-title {
  display: block; font-size: 14px; line-height: 20px;
  font-weight: 660; letter-spacing: -.1px; color: var(--ink);
}
.setup-body {
  display: block; margin-top: 2px;
  font-size: 12.5px; line-height: 18px; color: var(--ink-2);
  max-width: 62ch;
}
.setup-actions { flex: none; display: flex; align-items: center; gap: 6px; }

/* The one question the desk asks before it can do anything.
   Same skin as the field in the account menu, because it is the same question
   and a person meeting it twice should recognise it. */
.setup-input {
  display: block; width: 100%; max-width: 380px;
  margin-top: 9px; padding: 9px 12px;
  font: inherit; font-size: 13px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--r-lg); outline: none;
}
.setup-input::placeholder { color: var(--ink-4); }
.setup-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

@media (max-width: 599px) {
  .setup { flex-wrap: wrap; gap: 12px; }
  .setup-actions { width: 100%; }
  .setup-actions .btn { flex: 1; justify-content: center; }
}

/* ----------------------------------------------------------------- domains

   The panel exists to make one sentence true and readable: who holds this
   domain, where they are, what number to ring, and that this is the name that
   appears on your card. So the broker block is the only thing here with any
   weight on it, and the domain's own details sit above it quietly. */

.domains { list-style: none; margin: 0; padding: 0; }
.domain {
  padding: 16px 0;
  border-bottom: 1px solid var(--line-2);
}
.domain:last-child { border-bottom: 0; }

.domain-top { display: flex; align-items: baseline; gap: 10px; }
.domain-name {
  flex: 1; min-width: 0;
  font-size: 15px; line-height: 21px; font-weight: 660; letter-spacing: -.2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.domain-state {
  flex: none; font-size: 11.5px; font-weight: 660; letter-spacing: .02em;
  padding: 3px 8px; border-radius: var(--r-md);
  background: var(--line-2); color: var(--ink-3);
}
.domain-state.live { background: var(--good-soft); color: var(--good); }
.domain-state.unpaid { background: var(--warn-soft); color: var(--warn); }
.domain-state.bad { background: var(--bad-soft); color: var(--bad); }

.domain-when {
  margin-top: 3px; font-size: 12.5px; line-height: 18px;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}

/* The sentence. Bordered, because it is the reason the panel exists and should
   read as an answer rather than as more detail. */
.domain-broker {
  margin-top: 12px; padding: 12px 14px;
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: var(--r-lg);
  background: var(--bg);
}
.domain-broker.none { border-left-color: var(--line); }
.domain-broker-k {
  display: block; font-size: 10.5px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-4);
}
.domain-broker-n {
  display: block; margin-top: 2px;
  font-size: 14px; font-weight: 640; letter-spacing: -.1px; color: var(--ink);
}
.domain-broker-l {
  display: block; font-size: 12.5px; line-height: 18px; color: var(--ink-2);
  text-decoration: none;
}
a.domain-broker-l:hover { color: var(--accent); }
.domain-broker-c {
  display: block; margin-top: 7px;
  font-size: 12.5px; line-height: 18px; color: var(--ink-3);
}
.domain-note {
  margin: 10px 0 0; font-size: 12.5px; line-height: 18px; color: var(--ink-3);
}
.domain-form .tt-input { margin-bottom: 8px; }
/* The form that fixes the empty state sits inside the same box it replaces, so
   saying who holds a domain reads as answering the sentence above it. */
.domain-broker.none .tt-act { margin-top: 10px; }
.domain-broker.none .domain-form { margin-top: 10px; }
.domain-broker.none .domain-form .tt-input:first-child { margin-top: 0; }
.domain-form input[type="date"] { font-size: 13px; }
