/* =========================================================================
   ShipThat.io UI
   A dense workspace shell: two-tone plum navigation on the left, white grid
   on the right. The dark nav is the product's signature — the work surface
   stays paper-white so task rows read fast.
   ========================================================================= */

:root {
  /* Brand */
  --tf-violet: #7b68ee;
  --tf-violet-hover: #6350e2;
  --tf-violet-press: #5341c4;
  --tf-violet-tint: #f3f1fe;
  --tf-violet-tint-2: #e7e3fd;

  /* Navigation (two-tone: rail is the deepest layer, sidebar sits on top) */
  --tf-rail: #17121f;
  --tf-rail-hover: #241d31;
  --tf-rail-fg: #b6aecb;
  --tf-rail-fg-dim: #8b83a0;
  --tf-nav: #221b2e;
  --tf-nav-hover: rgba(255, 255, 255, 0.06);
  --tf-nav-line: rgba(255, 255, 255, 0.09);
  --tf-nav-fg: #ddd8ea;
  --tf-nav-fg-dim: #948ca9;
  --tf-nav-active: rgba(140, 122, 255, 0.20);
  --tf-nav-fg-strong: #ffffff;
  --tf-nav-field: rgba(255, 255, 255, 0.05);
  --tf-nav-field-focus: rgba(255, 255, 255, 0.09);
  --tf-nav-menu: #2c2439;
  --tf-violet-light: #b3a4ff;

  /* Text */
  --tf-ink: #292d34;
  --tf-ink-2: #5f6875;
  --tf-ink-3: #8f97a3;

  /* Surfaces */
  --tf-canvas: #ffffff;
  --tf-shell: #fafafb;
  --tf-hover: #f5f6f8;
  --tf-row-hover: #fbfbfd;
  --tf-chip-base: #ffffff;
  --tf-chip-ink: #191c22;
  --tf-line: #e8e9ee;
  --tf-line-soft: #f0f1f4;

  /* Semantic */
  --tf-red: #e5484d;
  --tf-red-tint: #fdf0f0;
  --tf-amber: #d98c14;
  --tf-green: #2eab6a;
  --tf-blue: #3b82f6;

  /* Type */
  --tf-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --tf-mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;

  /* Metrics */
  --tf-rail-w: 64px;
  --tf-side-w: 256px;
  --tf-topbar-h: 46px;
  --tf-row-h: 38px;
  --tf-radius: 8px;
  --tf-radius-sm: 6px;
  --tf-radius-lg: 12px;

  --tf-shadow-1: 0 1px 2px rgba(41, 45, 52, 0.06);
  --tf-shadow-2: 0 6px 16px rgba(41, 45, 52, 0.10);
  --tf-shadow-3: 0 18px 44px rgba(22, 25, 31, 0.20);

  --tf-ease: 120ms cubic-bezier(0.2, 0, 0.2, 1);

  /* Bootstrap bridge */
  --bs-body-font-family: var(--tf-ui);
  --bs-body-color: var(--tf-ink);
  --bs-border-radius: var(--tf-radius);
  --bs-border-color: var(--tf-line);
  --bs-link-color-rgb: 99, 80, 226;
  --bs-link-hover-color-rgb: 83, 65, 196;
}

/* ---------------------------------------------------------------- base ---- */

html { font-size: 14px; }

body {
  font-family: var(--tf-ui);
  font-size: 0.9286rem; /* 13px */
  line-height: 1.5;
  color: var(--tf-ink);
  background: var(--tf-canvas);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.003em;
}

h1, h2, h3, h4, h5 { letter-spacing: -0.018em; font-weight: 600; color: var(--tf-ink); }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.15rem; }
a { color: var(--tf-violet-hover); text-decoration: none; }
a:hover { color: var(--tf-violet-press); text-decoration: underline; }
code, .tf-mono { font-family: var(--tf-mono); font-size: 0.85em; }
hr { border-color: var(--tf-line); opacity: 1; }

:focus-visible {
  outline: 2px solid var(--tf-violet);
  outline-offset: 1px;
  border-radius: 4px;
}

.tf-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  stroke-width: 1.75;
  vertical-align: -3px;
}
.tf-icon-sm { width: 14px; height: 14px; }
.tf-icon-lg { width: 19px; height: 19px; }

.tf-muted { color: var(--tf-ink-2); }
.tf-dim { color: var(--tf-ink-3); }
.tf-label {
  font-size: 0.7857rem; /* 11px */
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--tf-ink-2);
  margin-bottom: 0.3rem;
}
.tf-eyebrow {
  font-size: 0.7857rem;
  color: var(--tf-ink-3);
  letter-spacing: 0.01em;
}

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

.tf-app-frame { display: flex; height: 100vh; overflow: hidden; background: var(--tf-canvas); }
body.tf-drawer-open { overflow: hidden; }

.tf-stage {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Icon rail */
.tf-rail {
  width: var(--tf-rail-w);
  flex: 0 0 var(--tf-rail-w);
  background: var(--tf-rail);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 0 8px;
  gap: 2px;
  position: sticky;
  top: 0;
  height: 100vh;
}

.tf-rail-logo {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  overflow: hidden;
  display: block;
  margin-bottom: 10px;
  background: #0a2a5a;
  box-shadow: 0 2px 8px rgba(10, 30, 70, 0.45);
}
.tf-rail-logo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 32%;
  display: block;
}
.tf-rail-logo:hover { filter: brightness(1.08); }

.tf-rail-item {
  width: 52px;
  padding: 7px 2px 5px;
  border: 0;
  background: none;
  border-radius: var(--tf-radius);
  color: var(--tf-rail-fg);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  font-size: 0.6429rem; /* 9px */
  font-weight: 500;
  letter-spacing: 0.005em;
  text-align: center;
  transition: background var(--tf-ease), color var(--tf-ease);
}
.tf-rail-item span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tf-rail-item:hover { background: var(--tf-rail-hover); color: #fff; text-decoration: none; }
.tf-rail-item.is-active { background: var(--tf-rail-hover); color: #fff; }
.tf-rail-item.is-active { background: var(--tf-nav); color: #fff; }
.tf-rail-item.is-active .tf-icon { color: var(--tf-violet-light); }
.tf-rail-spacer { flex: 1; }
.tf-rail-divider { width: 26px; height: 1px; background: rgba(255, 255, 255, 0.10); margin: 6px 0; }

/* Sidebar */
.tf-side {
  background: var(--tf-nav);
  color: var(--tf-nav-fg);
  --bs-offcanvas-bg: var(--tf-nav);
  --bs-offcanvas-color: var(--tf-nav-fg);
  --bs-offcanvas-width: min(300px, 86vw);
}
@media (min-width: 992px) {
  .tf-side {
    width: var(--tf-side-w);
    flex: 0 0 var(--tf-side-w);
    height: 100vh;
    display: flex;
    flex-direction: column;
  }
  /* Bootstrap clears the offcanvas background above the breakpoint. */
  .tf-side.offcanvas-lg { background-color: var(--tf-nav) !important; }
  [data-theme="light"] .tf-side.offcanvas-lg { border-right: 1px solid var(--tf-line); }
}
.tf-side-inner { display: flex; flex-direction: column; height: 100%; min-height: 0; }

.tf-ws-row { display: flex; align-items: center; gap: 4px; padding: 8px 8px 4px; }
.tf-ws {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: 0;
  background: none;
  border-radius: var(--tf-radius);
  color: var(--tf-nav-fg-strong);
  font-weight: 600;
  font-size: 0.9286rem;
  text-align: left;
  transition: background var(--tf-ease);
}
.tf-ws { color: var(--tf-nav-fg-strong); }
.tf-ws:hover { background: var(--tf-nav-hover); color: var(--tf-nav-fg-strong); }
.tf-ws::after { display: none; }
.tf-ws-mark {
  width: 22px;
  height: 22px;
  border-radius: var(--tf-radius-sm);
  background: var(--tf-violet);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 0.7857rem;
  font-weight: 700;
  flex: 0 0 auto;
}
/* ------ github links ------ */
.tf-gh-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.tf-gh-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid var(--tf-line);
  min-width: 0;
}
.tf-gh-row:last-child { border-bottom: 0; }
.tf-gh-kind { color: var(--tf-ink-3); display: inline-flex; flex: 0 0 auto; }
.tf-gh-title {
  color: var(--tf-ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.tf-gh-title:hover { color: var(--tf-violet); text-decoration: underline; }
.tf-gh-side { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.tf-gh-glyph { display: inline-flex; }
.tf-gh-row.is-gone .tf-gh-title { text-decoration: line-through; color: var(--tf-ink-3); }
/* Where a rewritten commit went. Quiet, because the row it sits on is the one
   somebody was looking for and this is only the forwarding address. */
.tf-gh-landed {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  font-size: 0.7857rem;
  color: var(--tf-ink-3);
}
.tf-gh-landed a { color: var(--tf-ink-2); font-family: var(--tf-mono); }
.tf-gh-landed a:hover { color: var(--tf-violet-hover); }
.tf-gh-unlink { opacity: 0; transition: opacity var(--tf-ease); }
.tf-gh-row:hover .tf-gh-unlink, .tf-gh-unlink:focus-visible { opacity: 1; }
.tf-gh-add { display: flex; gap: 8px; margin-top: 10px; }
.tf-gh-add input { flex: 1; min-width: 0; }

.tf-ws-icon { background: none; object-fit: cover; }
.tf-ws-chev { margin-left: auto; color: var(--tf-nav-fg-dim); flex: 0 0 auto; }
.tf-ws-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tf-side-search { padding: 4px 12px 10px; }
.tf-side-search .tf-search-wrap { position: relative; }
.tf-side-search .tf-icon {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--tf-nav-fg-dim);
  pointer-events: none;
}
.tf-side-search input {
  height: 32px;
  padding-left: 30px;
  font-size: 0.9286rem;
  background: var(--tf-nav-field);
  border-color: var(--tf-nav-line);
  color: var(--tf-nav-fg);
}
.tf-side-search input::placeholder { color: var(--tf-nav-fg-dim); }
.tf-side-search input:focus {
  background: var(--tf-nav-field-focus);
  border-color: var(--tf-violet);
  box-shadow: 0 0 0 3px rgba(123, 104, 238, 0.25);
  color: var(--tf-nav-fg-strong);
}

.tf-side-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 8px 8px; }
.tf-side-foot { border-top: 1px solid var(--tf-nav-line); padding: 8px; }

.tf-side-copy {
  margin: 6px 0 2px;
  padding: 0 8px;
  font-size: 0.6786rem;
  line-height: 1.3;
  color: var(--tf-nav-fg-dim);
  opacity: 0.8;
}

.tf-side-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 8px 4px;
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--tf-nav-fg-dim);
}
.tf-side-head .tf-side-add { margin-left: auto; }

.tf-side-link {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 6px 8px;
  border: 0;
  background: none;
  border-radius: var(--tf-radius-sm);
  color: var(--tf-nav-fg);
  font-size: 0.9286rem;
  font-weight: 500;
  text-align: left;
  transition: background var(--tf-ease), color var(--tf-ease);
}
.tf-side-link:hover { background: var(--tf-nav-hover); color: var(--tf-nav-fg-strong); text-decoration: none; }
.tf-side-link .tf-icon { color: var(--tf-nav-fg-dim); }
.tf-side-link.is-active { background: var(--tf-nav-active); color: var(--tf-nav-fg-strong); font-weight: 600; }
.tf-side-link.is-active .tf-icon { color: var(--tf-violet-light); }
.tf-side-link .tf-count {
  margin-left: auto;
  font-size: 0.7857rem;
  font-weight: 500;
  color: var(--tf-nav-fg-dim);
  font-variant-numeric: tabular-nums;
}
.tf-side-note { margin-left: auto; font-size: 0.7143rem; color: var(--tf-nav-fg-dim); }

.tf-side-add {
  border: 0;
  background: none;
  color: var(--tf-nav-fg-dim);
  padding: 2px;
  border-radius: 4px;
  line-height: 0;
  opacity: 0;
  transition: opacity var(--tf-ease), background var(--tf-ease);
}
.tf-side-add:hover { background: var(--tf-nav-hover); color: var(--tf-nav-fg-strong); }
.tf-side-head:hover .tf-side-add,
.tf-tree-summary:hover .tf-side-add,
.tf-side-add:focus-visible { opacity: 1; }

/* Project tree */
.tf-tree { margin-bottom: 2px; }
.tf-tree-summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 8px;
  border-radius: var(--tf-radius-sm);
  cursor: pointer;
  font-size: 0.9286rem;
  font-weight: 500;
  color: var(--tf-nav-fg);
  transition: background var(--tf-ease);
}
.tf-tree-summary::-webkit-details-marker { display: none; }
.tf-tree-summary:hover { background: var(--tf-nav-hover); color: var(--tf-nav-fg-strong); }
.tf-tree-summary .tf-icon { color: var(--tf-nav-fg-dim); }
.tf-tree-chev { color: var(--tf-nav-fg-dim); transition: transform var(--tf-ease); }
.tf-tree[open] > .tf-tree-summary .tf-tree-chev { transform: rotate(90deg); }
.tf-tree-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tf-tree-kids { padding-left: 14px; border-left: 1px solid var(--tf-nav-line); margin-left: 15px; }
.tf-tree-kids .tf-side-link { font-weight: 400; }
.tf-tree-kids .tf-side-link.is-active { font-weight: 600; }
.tf-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--tf-c, var(--tf-ink-3));
  flex: 0 0 auto;
}
.tf-side-empty { padding: 6px 9px; color: var(--tf-nav-fg-dim); font-size: 0.8571rem; }

/* Topbar */
.tf-topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--tf-topbar-h);
  flex: 0 0 var(--tf-topbar-h);
  padding: 0 12px 0 14px;
  background: var(--tf-canvas);
  border-bottom: 1px solid var(--tf-line);
}
.tf-crumbs {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-size: 0.9286rem;
}
.tf-crumb { color: var(--tf-ink-2); white-space: nowrap; }
.tf-crumb:hover { color: var(--tf-ink); text-decoration: none; }
.tf-crumb-sep { color: var(--tf-line); }
.tf-crumb-now {
  font-weight: 600;
  color: var(--tf-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-user-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  border: 0;
  background: none;
  padding: 4px 6px;
  border-radius: var(--tf-radius-sm);
  color: var(--tf-ink);
  font-size: 0.9286rem;
  font-weight: 500;
}
.tf-user-btn:hover { background: var(--tf-hover); }
.tf-user-btn::after { display: none; }

.tf-main { flex: 1; min-width: 0; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
.tf-main-flush { overflow: hidden; }
.tf-page-pad { padding: 22px 26px 48px; }
.tf-page-narrow { max-width: 44rem; }
.tf-page-mid { max-width: 66rem; }
/* Settings pages whose first card is a table: the rows need the room. */
.tf-page-wide { max-width: 82rem; }

.tf-page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.tf-page-head h1 { margin: 0; }
.tf-page-sub { color: var(--tf-ink-2); margin: 4px 0 0; max-width: 46rem; }

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

.btn {
  --bs-btn-font-size: 0.9286rem;
  --bs-btn-font-weight: 500;
  --bs-btn-border-radius: var(--tf-radius);
  --bs-btn-padding-y: 0.375rem;
  --bs-btn-padding-x: 0.75rem;
  transition: background var(--tf-ease), border-color var(--tf-ease), color var(--tf-ease);
}
.btn-sm { --bs-btn-font-size: 0.8571rem; --bs-btn-padding-y: 0.25rem; --bs-btn-padding-x: 0.6rem; }

.btn-primary {
  --bs-btn-bg: var(--tf-violet);
  --bs-btn-border-color: var(--tf-violet);
  --bs-btn-hover-bg: var(--tf-violet-hover);
  --bs-btn-hover-border-color: var(--tf-violet-hover);
  --bs-btn-active-bg: var(--tf-violet-press);
  --bs-btn-active-border-color: var(--tf-violet-press);
  --bs-btn-disabled-bg: var(--tf-violet-tint-2);
  --bs-btn-disabled-border-color: var(--tf-violet-tint-2);
  --bs-btn-font-weight: 600;
  box-shadow: var(--tf-shadow-1);
}

.btn-outline-secondary,
.tf-btn-quiet {
  --bs-btn-color: var(--tf-ink);
  --bs-btn-border-color: var(--tf-line);
  --bs-btn-bg: var(--tf-canvas);
  --bs-btn-hover-color: var(--tf-ink);
  --bs-btn-hover-bg: var(--tf-hover);
  --bs-btn-hover-border-color: #dcdee5;
  --bs-btn-active-color: var(--tf-ink);
  --bs-btn-active-bg: var(--tf-line-soft);
  --bs-btn-active-border-color: #dcdee5;
}
.tf-btn-quiet {
  border: 1px solid var(--bs-btn-border-color);
  background: var(--bs-btn-bg);
  color: var(--bs-btn-color);
}

.btn-outline-danger {
  --bs-btn-color: var(--tf-red);
  --bs-btn-border-color: #f3cdce;
  --bs-btn-hover-bg: var(--tf-red);
  --bs-btn-hover-border-color: var(--tf-red);
  --bs-btn-hover-color: #fff;
}
.btn-link { --bs-btn-color: var(--tf-ink-2); --bs-btn-hover-color: var(--tf-ink); text-decoration: none; }

/* Icon-only button */
.tf-icon-btn {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 0;
  background: none;
  border-radius: var(--tf-radius-sm);
  color: var(--tf-ink-2);
  transition: background var(--tf-ease), color var(--tf-ease);
}
.tf-icon-btn:hover { background: var(--tf-hover); color: var(--tf-ink); }

/* Toolbar chip */
.tf-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 9px;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-sm);
  background: var(--tf-canvas);
  color: var(--tf-ink);
  font-size: 0.8571rem;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--tf-ease), border-color var(--tf-ease);
}
.tf-chip:hover { background: var(--tf-hover); color: var(--tf-ink); text-decoration: none; }
.tf-chip::after { display: none; }
.tf-chip .tf-icon { color: var(--tf-ink-2); }
.tf-chip.is-on {
  background: var(--tf-violet-tint);
  border-color: var(--tf-violet-tint-2);
  color: var(--tf-violet-press);
}
.tf-chip.is-on .tf-icon { color: var(--tf-violet-hover); }
.tf-chip-key { color: var(--tf-ink-3); font-weight: 500; }

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

.form-control, .form-select {
  font-size: 0.9286rem;
  border-radius: var(--tf-radius-sm);
  border-color: var(--tf-line);
  color: var(--tf-ink);
  padding: 0.375rem 0.6rem;
  box-shadow: none;
  transition: border-color var(--tf-ease), box-shadow var(--tf-ease);
}
.form-control::placeholder { color: var(--tf-ink-3); }
.form-control:focus, .form-select:focus {
  border-color: var(--tf-violet);
  box-shadow: 0 0 0 3px var(--tf-violet-tint);
}
.form-control-sm, .form-select-sm { font-size: 0.8571rem; border-radius: var(--tf-radius-sm); }
.form-label { font-size: 0.8571rem; font-weight: 500; color: var(--tf-ink-2); margin-bottom: 0.25rem; }
.form-text { font-size: 0.8571rem; color: var(--tf-ink-3); }
.form-check-input { border-color: #ccd0d8; }
.form-check-input:checked { background-color: var(--tf-violet); border-color: var(--tf-violet); }
.form-check-input:focus { border-color: var(--tf-violet); box-shadow: 0 0 0 3px var(--tf-violet-tint); }
.form-check-label { font-size: 0.9286rem; }
.form-control-color { width: 3rem; padding: 0.25rem; }

.dropdown-menu {
  --bs-dropdown-font-size: 0.9286rem;
  --bs-dropdown-border-color: var(--tf-line);
  --bs-dropdown-border-radius: var(--tf-radius);
  --bs-dropdown-link-hover-bg: var(--tf-hover);
  --bs-dropdown-link-active-bg: var(--tf-violet-tint);
  --bs-dropdown-link-active-color: var(--tf-violet-press);
  --bs-dropdown-padding-y: 0.3rem;
  --bs-dropdown-item-padding-y: 0.35rem;
  --bs-dropdown-item-padding-x: 0.7rem;
  box-shadow: var(--tf-shadow-2);
  min-width: 12rem;
}
.dropdown-menu .dropdown-item { border-radius: var(--tf-radius-sm); width: auto; }
.dropdown-menu form { margin: 0; }
.dropdown-header {
  font-size: 0.7857rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--tf-ink-3);
  font-weight: 600;
}

.alert {
  --bs-alert-border-radius: var(--tf-radius);
  font-size: 0.9286rem;
  border-color: var(--tf-line);
  box-shadow: var(--tf-shadow-1);
}
.alert-success { background: #eefaf3; color: #1b6e46; border-color: #cceedd; }
.alert-danger { background: var(--tf-red-tint); color: #a3282c; border-color: #f6d5d6; }
.alert-warning { background: #fdf6e7; color: #8a5c07; border-color: #f5e4bc; }

/* Flash toast */
.tf-toasts {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 22px;
  margin: 0 auto;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: max-content;
  max-width: min(30rem, 92vw);
  pointer-events: none;
}
.tf-toast {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 14px;
  border-radius: 10px;
  background: #23262e;
  color: #f4f5f7;
  font-size: 0.9286rem;
  box-shadow: var(--tf-shadow-3);
  animation: tf-rise 180ms cubic-bezier(0.2, 0, 0.2, 1);
}
.tf-toast .tf-icon { flex: 0 0 auto; }
.tf-toast-ok .tf-icon { color: #6ee7a8; }
.tf-toast-bad .tf-icon { color: #ff9ea1; }
@keyframes tf-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------- cards --- */

.tf-card {
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-lg);
  box-shadow: var(--tf-shadow-1);
}
.tf-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--tf-line);
}
.tf-card-head h2 { font-size: 0.9286rem; font-weight: 600; margin: 0; }
.tf-card-body { padding: 16px; }
.tf-empty { padding: 34px 20px; text-align: center; color: var(--tf-ink-2); }
.tf-empty-title { font-weight: 600; color: var(--tf-ink); margin-bottom: 3px; }

/* Plain data table (settings screens) */
.tf-table { width: 100%; margin: 0; }
.tf-table th {
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--tf-ink-3);
  padding: 9px 16px;
  border-bottom: 1px solid var(--tf-line);
  white-space: nowrap;
}
.tf-table td {
  padding: 10px 16px;
  border-bottom: 1px solid var(--tf-line-soft);
  vertical-align: middle;
}
.tf-table tbody tr:last-child td { border-bottom: 0; }
.tf-table tbody tr:hover td { background: var(--tf-row-hover); }
.tf-table .tf-cell-count { width: 11rem; }
.tf-actions { display: flex; gap: 6px; justify-content: flex-end; align-items: center; }
.tf-actions form { margin: 0; }

/* Per-member permissions, folded away until somebody wants them */
.tf-perms { min-width: 17rem; }
.tf-perms-summary {
  display: flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  list-style: none;
  color: var(--tf-ink-2);
  font-size: 0.8571rem;
  padding: 2px 0;
}
.tf-perms-summary::-webkit-details-marker { display: none; }
.tf-perms-summary:hover { color: var(--tf-ink); }
.tf-perms-summary svg { flex: none; transition: transform var(--tf-ease); }
.tf-perms[open] > .tf-perms-summary svg { transform: rotate(90deg); }
.tf-perms-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr));
  gap: 6px 14px;
  margin-top: 9px;
  padding: 10px 12px;
  border: 1px solid var(--tf-line);
  border-radius: 8px;
}
.tf-perm { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; }
.tf-perm .form-check-input { margin: 2px 0 0; flex: none; }
.tf-perm-label { display: block; font-size: 0.8571rem; line-height: 1.3; }
.tf-perm-hint { display: block; font-size: 0.7857rem; color: var(--tf-ink-3); line-height: 1.3; }


/* ------------------------------------------------------------ task types --- */

.tf-type {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
  white-space: nowrap;
}
.tf-type i {
  color: var(--tf-c, var(--tf-ink-3));
  font-size: 0.8571rem;
  width: 14px;
  text-align: center;
}
.tf-type-bug { color: var(--tf-red); font-weight: 500; }
.dropdown-item .fa-solid, .tf-chip .fa-solid { width: 14px; text-align: center; }

/* ------------------------------------------------------------- sub-tasks --- */

.tf-subtask-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: var(--tf-radius-sm);
  background: none;
  color: var(--tf-ink-3);
  transition: transform var(--tf-ease), color var(--tf-ease), background var(--tf-ease);
}
.tf-subtask-toggle:hover { background: var(--tf-violet-tint); color: var(--tf-violet-press); }
.tf-subtask-toggle.is-collapsed { transform: rotate(-90deg); }
.tf-subtask-mark { display: inline-flex; color: #d5d9e0; }

.tf-cell-title.is-sub { padding-left: 18px; }
.tf-subrow > td:first-child { box-shadow: inset 2px 0 0 var(--tf-violet-tint-2); }
.tf-subrow .tf-task-name { font-weight: 400; }

.tf-subtask-count {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 18px;
  padding: 0 6px 0 4px;
  border-radius: 999px;
  background: var(--tf-line-soft);
  color: var(--tf-ink-2);
  font-size: 0.7143rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.tf-add-row.is-sub .tf-add-form input[type="text"] { padding-left: 64px; font-size: 0.8571rem; }

.tf-subtask-list { list-style: none; margin: 0 0 10px; padding: 0; }
.tf-subtask {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 2px;
  border-bottom: 1px solid var(--tf-line-soft);
}
.tf-subtask:last-child { border-bottom: 0; }
.tf-subtask-tick { margin: 0; display: flex; }
.tf-tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid #d5d9e0;
  border-radius: 6px;
  background: var(--tf-canvas);
  color: transparent;
  transition: all var(--tf-ease);
}
.tf-tick:hover { border-color: var(--tf-violet); color: var(--tf-violet-light); }
.tf-tick.is-on { background: var(--tf-green); border-color: var(--tf-green); color: #fff; }
.tf-subtask-name { color: var(--tf-ink); font-size: 0.9286rem; min-width: 0; flex: 1 1 auto; }
.tf-subtask-name:hover { color: var(--tf-violet-press); }
.tf-subtask.is-done .tf-subtask-name { color: var(--tf-ink-3); text-decoration: line-through; }
.tf-subtask-side { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; flex: 0 0 auto; }
.tf-subtask-add { display: flex; align-items: center; gap: 8px; }
.tf-subtask-add input[type="text"] { flex: 1 1 auto; }

/* --------------------------------------------------------- agent marks --- */

.tf-avatar-preset { padding: 0; box-shadow: 0 0 0 1px rgba(15, 17, 21, 0.12); }
.tf-avatar-preset .tf-mark { display: block; line-height: 1; }

.tf-preset-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.tf-preset {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  width: 5.1rem;
  padding: 9px 4px;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  font-size: 0.7857rem;
  color: var(--tf-ink-2);
  cursor: pointer;
  transition: border-color var(--tf-ease), background var(--tf-ease);
}
.tf-preset:hover { border-color: var(--tf-violet-light); }
.tf-preset:has(input:checked) {
  border-color: var(--tf-violet);
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press);
  font-weight: 600;
}
.tf-preset:has(input:focus-visible) { outline: 2px solid var(--tf-violet); outline-offset: 2px; }

/* ---------------------------------------------------------- badges/meta --- */

.tf-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 9px 0 7px;
  border-radius: 999px;
  font-size: 0.7857rem;
  font-weight: 600;
  white-space: nowrap;
  background: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 13%, var(--tf-chip-base));
  color: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 72%, var(--tf-chip-ink));
}
.tf-status::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--tf-c, var(--tf-ink-3));
}
.tf-status-solid {
  display: inline-flex;
  align-items: center;
  height: 21px;
  padding: 0 8px;
  border-radius: var(--tf-radius-sm);
  background: var(--tf-c, var(--tf-ink-3));
  color: #fff;
  font-size: 0.7857rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.tf-priority {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
  white-space: nowrap;
}
.tf-priority .tf-icon { color: var(--tf-p, var(--tf-ink-3)); }
.tf-priority-urgent { --tf-p: var(--tf-red); color: var(--tf-red); font-weight: 600; }
.tf-priority-high { --tf-p: var(--tf-amber); color: #8a5c07; font-weight: 500; }
.tf-priority-normal { --tf-p: var(--tf-blue); }
.tf-priority-low { --tf-p: #9aa2ae; }
.tf-priority-none { --tf-p: #ccd0d8; color: var(--tf-ink-3); }

.tf-due { white-space: nowrap; display: inline-flex; align-items: center; gap: 5px; font-size: 0.8571rem; color: var(--tf-ink-2); }
.tf-due.is-overdue { color: var(--tf-red); font-weight: 500; }
.tf-due.is-soon { color: #8a5c07; font-weight: 500; }
.tf-due-empty { color: var(--tf-ink-3); font-size: 0.8571rem; }
.tf-ref { font-family: var(--tf-mono); font-size: 0.7857rem; color: var(--tf-ink-3); white-space: nowrap; }

/* A reference and the button that copies it. The button is the quiet kind:
   present enough to find, faint enough that a column of them does not read as
   a column of buttons. */
.tf-ref-cell { display: inline-flex; align-items: center; gap: 2px; min-width: 0; }
.tf-ref-copy {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--tf-ink-3);
  cursor: pointer;
  transition: opacity var(--tf-ease), background var(--tf-ease), color var(--tf-ease);
}
/* On a board it waits for the row to be hovered — a column of buttons down
   the page would read as a column of buttons. Everywhere else there is one of
   them and nothing to hover, so it simply shows. */
.tf-row .tf-ref-copy { opacity: 0; }
.tf-row:hover .tf-ref-copy,
.tf-row .tf-ref-copy:focus-visible,
.tf-row .tf-ref-copy.is-copied,
.tf-row .tf-ref-copy.is-copy-failed { opacity: 1; }
.tf-ref-copy:hover { background: var(--tf-hover); color: var(--tf-ink); }
.tf-ref-copy .tf-copy-done { display: none; }
.tf-ref-copy.is-copied { color: var(--tf-green); }
.tf-ref-copy.is-copied .tf-copy-idle { display: none; }
.tf-ref-copy.is-copied .tf-copy-done { display: block; }
.tf-ref-copy.is-copy-failed { color: var(--tf-red); }

.tf-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  object-fit: cover;
  font-size: 0.6429rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: #fff;
  background: hsl(var(--tf-hue, 250), 45%, 52%);
  box-shadow: 0 0 0 2px var(--tf-canvas);
  flex: 0 0 auto;
}
.tf-avatar-empty {
  width: 22px;
  height: 22px;
  border: 1px dashed #d5d9e0;
  background: none;
  box-shadow: none;
}
.tf-assignees { display: inline-flex; align-items: center; padding-left: 5px; }
.tf-assignees .tf-avatar { margin-left: -5px; }
.tf-assignees-more {
  display: inline-grid;
  place-items: center;
  min-width: 22px;
  height: 22px;
  margin-left: -5px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--tf-line-soft);
  color: var(--tf-ink-2);
  font-size: 0.6429rem;
  font-weight: 700;
  box-shadow: 0 0 0 2px var(--tf-canvas);
}

/* ------------------------------------------------------------ list view --- */

.tf-board { display: flex; flex-direction: column; flex: 1; min-height: 0; }

.tf-board-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px 10px;
}
.tf-board-title { font-size: 1.375rem; font-weight: 650; letter-spacing: -0.022em; margin: 0; }
.tf-board-count {
  font-size: 0.8571rem;
  color: var(--tf-ink-3);
  font-variant-numeric: tabular-nums;
  padding-bottom: 2px;
}

.tf-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0 20px 12px;
  border-bottom: 1px solid var(--tf-line);
}
.tf-toolbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.tf-toolbar .tf-search-wrap { position: relative; }
.tf-toolbar .tf-search-wrap .tf-icon {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--tf-ink-3);
  pointer-events: none;
}
.tf-toolbar .tf-search-wrap input {
  height: 30px;
  width: 210px;
  padding-left: 30px;
  font-size: 0.8571rem;
  border-radius: var(--tf-radius-sm);
}
.tf-col-menu { padding: 6px; min-width: 13rem; }
.tf-col-menu label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: var(--tf-radius-sm);
  font-size: 0.9286rem;
  cursor: pointer;
}
.tf-col-menu label:hover { background: var(--tf-hover); }

.tf-table-wrap { flex: 1; overflow: auto; min-height: 0; }
.tf-grid {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--tf-canvas);
  font-size: 0.9286rem;
}
.tf-grid thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--tf-canvas);
  border-bottom: 1px solid var(--tf-line);
  padding: 0 10px;
  height: 32px;
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--tf-ink-3);
  text-align: left;
  white-space: nowrap;
  user-select: none;
}
/* No `position: relative` here — it would beat the sticky above and drop the
   header out of the top of the board. Sticky is already a positioned value,
   so the resize grip has its containing block either way. */
.tf-grid thead th[data-col] { cursor: grab; }
.tf-grid .tf-col-label { display: block; overflow: hidden; text-overflow: ellipsis; }

/* The edge of a header, widened into something a pointer can find. Sitting
   half outside its own cell puts the grip on the line between two columns,
   which is where a hand reaches for it. */
.tf-col-resize {
  position: absolute;
  top: 0;
  right: -3px;
  z-index: 1;
  width: 7px;
  height: 100%;
  cursor: col-resize;
}
.tf-col-resize::after {
  content: "";
  position: absolute;
  top: 6px;
  bottom: 6px;
  left: 3px;
  width: 1px;
  background: var(--tf-line);
  opacity: 0;
  transition: opacity var(--tf-ease), background var(--tf-ease);
}
.tf-grid thead th:hover .tf-col-resize::after { opacity: 1; }
.tf-col-resize:hover::after { background: var(--tf-violet); opacity: 1; }
/* While a column is being dragged wider the pointer leaves the grip behind,
   so the cursor is held on the whole page rather than on the handle. */
body.is-resizing-column { cursor: col-resize; user-select: none; }
body.is-resizing-column .tf-col-resize::after { background: var(--tf-violet); opacity: 1; }

.tf-grid th[data-col="title"] { min-width: 22rem; }
.tf-grid th[data-col="type"] { width: 7.5rem; }
.tf-grid th[data-col="ref"] { width: 8rem; }
.tf-grid th[data-col="assignees"] { width: 8rem; }
.tf-grid th[data-col="status"] { width: 9rem; }
.tf-grid th[data-col="priority"] { width: 7.5rem; }
.tf-grid th[data-col="due_date"] { width: 8rem; }
.tf-grid th[data-col="list"] { width: 10rem; }
.tf-grid th[data-col^="cf_"] { width: 8rem; }
.tf-col-check, .tf-col-handle { width: 26px; padding: 0 !important; }
.tf-col-check { padding-left: 12px !important; }

/* Group header */
.tf-group-row td {
  padding: 0;
  border-bottom: 1px solid var(--tf-line);
  background: var(--tf-canvas);
}
.tf-group {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 12px 8px;
  background: var(--tf-canvas);
}
.tf-group-toggle {
  border: 0;
  background: none;
  padding: 2px;
  border-radius: 4px;
  color: var(--tf-ink-3);
  line-height: 0;
  transition: transform var(--tf-ease), background var(--tf-ease);
}
.tf-group-toggle:hover { background: var(--tf-hover); color: var(--tf-ink); }
.tf-group-toggle .tf-icon { transition: transform var(--tf-ease); }
.tf-group-count {
  font-size: 0.8571rem;
  color: var(--tf-ink-3);
  font-variant-numeric: tabular-nums;
}
.tf-group-add {
  margin-left: 4px;
  opacity: 0;
  transition: opacity var(--tf-ease);
}
.tf-group:hover .tf-group-add, .tf-group-add:focus-visible { opacity: 1; }
tbody.is-collapsed tr:not(.tf-group-row) { display: none; }
tbody.is-collapsed .tf-group-toggle .tf-icon { transform: rotate(-90deg); }
tbody + tbody .tf-group-row td { border-top: 1px solid var(--tf-line-soft); }

/* Rows */
.tf-row td {
  height: var(--tf-row-h);
  padding: 0 10px;
  border-bottom: 1px solid var(--tf-line-soft);
  vertical-align: middle;
  background: var(--tf-canvas);
  transition: background var(--tf-ease);
}
.tf-row:hover td { background: var(--tf-row-hover); }
.tf-row.is-selected td { background: var(--tf-violet-tint); }
.tf-row .form-check-input { opacity: 0; transition: opacity var(--tf-ease); }
.tf-row:hover .form-check-input,
.tf-row .form-check-input:checked,
.tf-row .form-check-input:focus-visible { opacity: 1; }
.tf-drag {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 24px;
  cursor: grab;
  color: #c4c9d2;
  opacity: 0;
  transition: opacity var(--tf-ease);
}
.tf-row:hover .tf-drag { opacity: 1; }
.tf-drag:active { cursor: grabbing; }
.sortable-ghost td { background: var(--tf-violet-tint) !important; }
.sortable-chosen { box-shadow: var(--tf-shadow-2); }

.tf-cell-title { display: flex; align-items: center; gap: 8px; min-width: 0; }
.tf-task-name {
  color: var(--tf-ink);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-task-name:hover { color: var(--tf-violet-press); text-decoration: none; }
.tf-row.is-done .tf-task-name { color: var(--tf-ink-2); }
.tf-task-meta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 0.7857rem;
  color: var(--tf-ink-3);
  flex: 0 0 auto;
}
.tf-row-open {
  margin-left: auto;
  opacity: 0;
  flex: 0 0 auto;
  transition: opacity var(--tf-ease);
}
.tf-row:hover .tf-row-open, .tf-row-open:focus-visible { opacity: 1; }
.tf-row-where { font-size: 0.7857rem; color: var(--tf-ink-3); }

.tf-cell-edit { cursor: pointer; }
.tf-cell-edit:hover { box-shadow: inset 0 0 0 1px var(--tf-line); border-radius: 4px; }
.tf-cell-empty { color: var(--tf-ink-3); }

/* A dropdown value fills its column instead of hugging its text. The colour is
   the point of a dropdown field, and a six-character pill floating in a wide
   column was easy to miss entirely. Scoped to the custom-field cells so the
   status pill, tags and every other badge keep the shape they had. Block
   rather than flex so a long value can still end in an ellipsis. */
.tf-grid td[data-col^="cf_"] .tf-status {
  display: block;
  width: 100%;
  height: 24px;
  line-height: 24px;
  padding: 0 9px;
  border-radius: 5px;
  overflow: hidden;
  text-overflow: ellipsis;
  background: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 22%, var(--tf-chip-base));
}
.tf-inline-input {
  width: 100% !important;
  min-width: 0 !important;
  height: 26px;
  padding: 0 6px;
  font-size: 0.8571rem;
  line-height: 1.2;
  box-sizing: border-box;
}
.tf-assignee-editor {
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  box-shadow: var(--tf-shadow-2);
  padding: 6px;
  min-width: 190px;
}
.tf-check-line {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 6px;
  border-radius: var(--tf-radius-sm);
  font-size: 0.9286rem;
  cursor: pointer;
}
.tf-check-line:hover { background: var(--tf-hover); }

/* Inline add row */
.tf-add-row td { padding: 0; border-bottom: 1px solid var(--tf-line-soft); background: var(--tf-canvas); }
.tf-add-form { display: flex; align-items: center; }
.tf-add-form input[type="text"] {
  border: 0;
  border-radius: 0;
  background: none;
  height: 34px;
  padding-left: 46px;
  font-size: 0.9286rem;
  box-shadow: none;
  color: var(--tf-ink);
}
.tf-add-form input[type="text"]::placeholder { color: var(--tf-ink-3); }
.tf-add-form input[type="text"]:focus { background: var(--tf-hover); box-shadow: none; }

.tf-empty-cell { padding: 44px 20px; text-align: center; color: var(--tf-ink-2); }

/* Bulk bar */
.tf-bulk-dock {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 20px;
  z-index: 30;
  display: flex;
  justify-content: center;
  padding: 0 12px;
  pointer-events: none;
}
.tf-bulk {
  pointer-events: auto;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 8px 10px 8px 14px;
  border-radius: 12px;
  background: #23262e;
  color: #f4f5f7;
  box-shadow: var(--tf-shadow-3);
  overflow-x: auto;
  scrollbar-width: thin;
  animation: tf-rise 160ms cubic-bezier(0.2, 0, 0.2, 1);
}
.tf-bulk-count { font-size: 0.8571rem; font-weight: 600; margin-right: 4px; white-space: nowrap; }
.tf-bulk .form-select {
  flex: 0 0 auto;
  height: 28px;
  padding: 0 1.6rem 0 0.5rem;
  font-size: 0.8571rem;
  background-color: #32363f;
  border-color: #434852;
  color: #f4f5f7;
  max-width: 8.5rem;
}
.tf-bulk .form-select:focus { border-color: var(--tf-violet); box-shadow: 0 0 0 3px rgba(123, 104, 238, 0.3); }
.tf-bulk .tf-bulk-btn {
  flex: 0 0 auto;
  height: 28px;
  padding: 0 9px;
  border: 0;
  border-radius: var(--tf-radius-sm);
  background: #32363f;
  color: #e9eaee;
  font-size: 0.8571rem;
  font-weight: 500;
  transition: background var(--tf-ease);
}
.tf-bulk .tf-bulk-btn:hover { background: #40454f; color: #fff; }
.tf-bulk .tf-bulk-btn.is-danger:hover { background: var(--tf-red); }
.tf-bulk-sep { width: 1px; height: 20px; background: #434852; margin: 0 2px; }

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

.tf-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(24, 27, 33, 0.32);
  z-index: 40;
  animation: tf-fade 140ms ease;
}
@keyframes tf-fade { from { opacity: 0; } to { opacity: 1; } }
.tf-drawer {
  position: fixed;
  top: 0;
  right: 0;
  width: min(760px, 100%);
  height: 100vh;
  background: var(--tf-canvas);
  z-index: 50;
  display: flex;
  flex-direction: column;
  box-shadow: var(--tf-shadow-3);
  animation: tf-slide 180ms cubic-bezier(0.2, 0, 0.2, 1);
}
@keyframes tf-slide { from { transform: translateX(24px); opacity: 0.4; } to { transform: none; opacity: 1; } }
.tf-drawer turbo-frame { display: flex; flex-direction: column; min-height: 0; flex: 1; }

.tf-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.tf-panel-top {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px 10px 16px;
  border-bottom: 1px solid var(--tf-line);
  flex: 0 0 auto;
}
.tf-panel-scroll { overflow-y: auto; padding: 16px 20px 28px; }
.tf-panel-title {
  width: 100%;
  border: 0;
  padding: 2px 0 10px;
  font-size: 1.25rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  color: var(--tf-ink);
  background: none;
  resize: none;
  overflow: hidden;
}
.tf-panel-title:focus { outline: none; box-shadow: none; }

/* The panel's sections, one at a time. The strip stays put while a tab
   scrolls, because the way back to the properties should not be a scroll up. */
.tf-panel-tabs { margin-top: 10px; }
.tf-tabs {
  position: sticky;
  top: -16px;
  z-index: 3;
  display: flex;
  gap: 2px;
  margin: 0 -20px 14px;
  padding: 6px 20px 0;
  background: var(--tf-canvas);
  border-bottom: 1px solid var(--tf-line);
}
.tf-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 10px 8px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--tf-ink-2);
  font-size: 0.8571rem;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--tf-ease), border-color var(--tf-ease);
}
.tf-tab .tf-icon { color: var(--tf-ink-3); }
.tf-tab:hover { color: var(--tf-ink); }
.tf-tab.is-on { color: var(--tf-violet-press); border-bottom-color: var(--tf-violet); }
.tf-tab.is-on .tf-icon { color: var(--tf-violet); }
.tf-tab-count {
  min-width: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--tf-hover);
  color: var(--tf-ink-2);
  font-size: 0.7143rem;
  font-variant-numeric: tabular-nums;
}
.tf-tab.is-on .tf-tab-count { background: var(--tf-violet-tint-2); color: var(--tf-violet-press); }
.tf-tab-dot {
  padding: 0 5px;
  border-radius: 999px;
  background: rgba(217, 140, 20, 0.16);
  color: var(--tf-amber);
  font-size: 0.7143rem;
  font-weight: 600;
}
.tf-pane[hidden] { display: none; }
.tf-meta-grid {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 8px 12px;
  align-items: center;
  padding: 4px 0 14px;
  border-bottom: 1px solid var(--tf-line);
  margin-bottom: 16px;
}
.tf-meta-key {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
}
.tf-meta-key .tf-icon { color: var(--tf-ink-3); }
.tf-meta-val .form-select, .tf-meta-val .form-control { height: 30px; max-width: 17rem; padding-top: 0; padding-bottom: 0; }
.tf-section { margin-bottom: 20px; }
.tf-section-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--tf-ink-3);
  margin-bottom: 8px;
}
.tf-prose { color: var(--tf-ink); }
.tf-prose :last-child { margin-bottom: 0; }
.tf-prose pre {
  background: var(--tf-shell);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-sm);
  padding: 10px 12px;
  overflow: auto;
}
.tf-prose code { background: var(--tf-violet-tint); padding: 1px 4px; border-radius: 4px; color: var(--tf-violet-press); }
.tf-prose pre code { background: none; padding: 0; color: inherit; }

.tf-comment { display: flex; gap: 10px; padding: 9px 0; }
.tf-comment-body { min-width: 0; flex: 1; }
.tf-comment-head { display: flex; align-items: baseline; gap: 7px; }
.tf-comment-who { font-weight: 600; font-size: 0.8571rem; }
.tf-comment-when { font-size: 0.7857rem; color: var(--tf-ink-3); }
.tf-comment-text { margin: 2px 0 0; }
.tf-comment-text p:last-child { margin-bottom: 0; }
.tf-comment-box { border: 1px solid var(--tf-line); border-radius: var(--tf-radius); padding: 8px; background: var(--tf-canvas); }
.tf-comment-box textarea { border: 0; box-shadow: none; padding: 2px 4px; resize: vertical; }
.tf-comment-box textarea:focus { box-shadow: none; }
.tf-comment-box-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding-top: 6px; }
.tf-composer-hint { margin-right: auto; font-size: 0.7857rem; }

/* A question is a comment that somebody is still waiting on, so it is marked
   where the comment's own name is and carries its state under it. Answered, it
   goes quiet again — the thread should show what is outstanding, not shout
   about what has been settled. */
.tf-q { border-left: 2px solid rgba(217, 140, 20, 0.5); padding-left: 10px; margin-left: -12px; }
.tf-q.is-answered { border-left-color: var(--tf-line); }
.tf-q-badge, .tf-a-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 18px;
  padding: 0 6px 0 4px;
  border-radius: 999px;
  font-size: 0.7143rem;
  font-weight: 600;
}
.tf-q-badge { background: rgba(217, 140, 20, 0.14); color: var(--tf-amber); }
.tf-a-badge { background: rgba(46, 171, 106, 0.14); color: var(--tf-green); }
.tf-q-ref { font-size: 0.7857rem; margin-top: 1px; }
.tf-q-link { color: var(--tf-ink-3); }
.tf-q-link:hover { color: var(--tf-violet-hover); }
.tf-q-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.tf-q-state { display: inline-flex; align-items: center; gap: 5px; font-size: 0.7857rem; }
.tf-q-state.is-open { color: var(--tf-amber); }
.tf-q-state.is-answered { color: var(--tf-ink-3); }
.tf-answer-box { flex: 1 1 100%; margin-top: 6px; }
.tf-q-open-list {
  margin-bottom: 12px;
  padding: 8px 10px;
  border: 1px solid rgba(217, 140, 20, 0.35);
  border-radius: var(--tf-radius);
  background: rgba(217, 140, 20, 0.07);
}
.tf-q-open-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.7857rem;
  font-weight: 600;
  color: var(--tf-amber);
  margin-bottom: 4px;
}
.tf-q-open-row { display: flex; gap: 6px; padding: 2px 0; font-size: 0.8571rem; color: var(--tf-ink-2); }
.tf-q-open-row:hover { color: var(--tf-violet-hover); }
.tf-q-open-who { font-weight: 600; white-space: nowrap; }
.tf-q-open-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tf-image-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }
.tf-image-grid img {
  width: 100%;
  height: 84px;
  object-fit: cover;
  border-radius: var(--tf-radius-sm);
  border: 1px solid var(--tf-line);
}
.tf-drop {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border: 1px dashed var(--tf-line);
  border-radius: var(--tf-radius);
  background: var(--tf-shell);
}
.tf-drop input[type="file"] { border: 0; background: none; font-size: 0.8571rem; padding: 0; }

/* ----------------------------------------------------------------- auth --- */

.tf-auth {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
  background:
    radial-gradient(1200px 520px at 12% -8%, #efecfe 0%, rgba(255, 255, 255, 0) 62%),
    radial-gradient(900px 460px at 96% 108%, #eaf1fe 0%, rgba(255, 255, 255, 0) 60%),
    var(--tf-shell);
}
.tf-auth-card {
  width: min(400px, 100%);
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: 16px;
  padding: 28px 26px 24px;
  box-shadow: 0 20px 50px rgba(30, 26, 60, 0.10);
}
.tf-auth-card h1 { font-size: 1.3125rem; margin: 14px 0 4px; }
.tf-auth-card .tf-page-sub { margin-bottom: 20px; }
.tf-auth-mark {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  object-fit: cover;
  object-position: 50% 32%;
  display: block;
}
.tf-auth-foot { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--tf-line); font-size: 0.8571rem; color: var(--tf-ink-2); }
.tf-auth-foot a { font-weight: 500; }
.tf-auth-foot p { margin-bottom: 4px; }

.tf-token {
  font-family: var(--tf-mono);
  font-size: 0.8571rem;
  word-break: break-all;
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-sm);
  padding: 8px 10px;
}

/* --------------------------------------------------------------- admin --- */

/* Storage accounting reads as figures first: the stat row carries the number
   a customer gets billed for, the table explains where it came from. */
.tf-stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 12px;
  margin-bottom: 20px;
}
.tf-stat {
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.tf-stat-key { font-size: 0.7857rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--tf-ink-3); }
.tf-stat-val { font-size: 1.375rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.tf-stat-note { font-size: 0.7857rem; color: var(--tf-ink-3); }

.tf-table-admin td { font-variant-numeric: tabular-nums; }

.tf-meter {
  display: block;
  width: 100%;
  min-width: 4rem;
  height: 6px;
  border-radius: 999px;
  background: var(--tf-line-soft);
  overflow: hidden;
}
.tf-meter-fill { display: block; height: 100%; background: var(--tf-violet); border-radius: 999px; }

.tf-toggle {
  border: 1px solid var(--tf-line);
  background: var(--tf-canvas);
  color: var(--tf-ink-2);
  border-radius: 999px;
  padding: 2px 12px;
  font-size: 0.7857rem;
  font-weight: 500;
}
.tf-toggle:hover { border-color: var(--tf-violet); color: var(--tf-violet); }
.tf-toggle.is-on { background: var(--tf-violet-tint); border-color: var(--tf-violet-tint-2); color: var(--tf-violet-press); }

/* A guest is told plainly, once, why the controls are missing. */
.tf-readonly {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.7857rem;
  color: var(--tf-ink-2);
  background: var(--tf-shell);
  border: 1px solid var(--tf-line);
  border-radius: 999px;
  padding: 3px 10px;
}
.tf-readonly-note { font-size: 0.8571rem; color: var(--tf-ink-3); margin: 0; }

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

.tf-side-close { display: none; }

@media (max-width: 991.98px) {
  .tf-rail { display: none; }
  .tf-side.offcanvas-lg { border-right: 1px solid var(--tf-line); }
  .tf-app-frame { height: 100dvh; }
  .tf-side-close { display: inline-grid; }
  .tf-stage { border-left: 0; }
  .tf-page-pad { padding: 18px 16px 40px; }
  .tf-board-head, .tf-toolbar { padding-left: 14px; padding-right: 14px; }
  .tf-meta-grid { grid-template-columns: 1fr; gap: 4px; }
  .tf-meta-key { margin-top: 8px; }
  .tf-meta-fields { grid-template-columns: 1fr; }
}

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

/* Drag handles only appear where reordering is persisted. */
.tf-board:not(.is-sortable) .tf-drag { display: none; }

/* Toolbar controls sit at chip height. */
.tf-toolbar .form-select {
  height: 30px;
  width: auto;
  padding: 0 1.75rem 0 0.55rem;
  font-size: 0.8571rem;
  border-radius: var(--tf-radius-sm);
}

/* Workspace switcher popover, matched to the dark navigation. */
.tf-nav-menu {
  --bs-dropdown-bg: var(--tf-nav-menu);
  --bs-dropdown-color: var(--tf-nav-fg);
  --bs-dropdown-border-color: var(--tf-nav-line);
  --bs-dropdown-link-color: var(--tf-nav-fg);
  --bs-dropdown-link-hover-color: var(--tf-nav-fg-strong);
  --bs-dropdown-link-hover-bg: var(--tf-nav-hover);
  --bs-dropdown-link-active-bg: var(--tf-nav-active);
  --bs-dropdown-link-active-color: var(--tf-nav-fg-strong);
  --bs-dropdown-divider-bg: var(--tf-nav-line);
  --bs-dropdown-header-color: var(--tf-nav-fg-dim);
}

/* Images pasted into descriptions and comments. */
.tf-prose img {
  max-width: 100%;
  height: auto;
  border-radius: var(--tf-radius-sm);
  border: 1px solid var(--tf-line);
  margin: 6px 0;
}
.tf-comment-text.tf-prose img { max-width: min(100%, 22rem); }
textarea.is-dropping {
  border-color: var(--tf-violet);
  box-shadow: 0 0 0 3px var(--tf-violet-tint);
}

/* What you just pasted, under the field it went into: a description or a
   comment is markdown until it is saved, so without this the only sign an
   image had arrived was the link text standing in for it. */
.tf-paste-shelf { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.tf-paste-shelf:empty { display: none; }
.tf-paste-chip {
  margin: 0;
  width: 104px;
  font-size: 0.7143rem;
  color: var(--tf-ink-3);
}
.tf-paste-chip img {
  width: 100%;
  height: 64px;
  object-fit: cover;
  border-radius: var(--tf-radius-sm);
  border: 1px solid var(--tf-line);
  opacity: 0.5;
  transition: opacity 140ms ease;
}
.tf-paste-chip figcaption {
  padding-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-paste-chip.is-ready img { opacity: 1; }
.tf-paste-chip.is-failed img { opacity: 0.25; border-color: var(--tf-red); }
.tf-paste-chip.is-failed figcaption { color: var(--tf-red); white-space: normal; }

/* ------------------------------------------------------------- trix ----- */

/* The description editor. actiontext.css lands first (app stylesheets load in
   name order), so these are the overrides on top of Trix's own defaults.
   Everything is written in the theme's variables, which is all dark mode
   needs. */

trix-editor.tf-trix {
  font-size: 0.9286rem;
  line-height: 1.55;
  min-height: 9rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-sm);
  background: var(--tf-paper, #fff);
  color: var(--tf-ink);
  transition: border-color var(--tf-ease), box-shadow var(--tf-ease);
}
trix-editor.tf-trix:focus {
  border-color: var(--tf-violet);
  box-shadow: 0 0 0 3px var(--tf-violet-tint);
  outline: none;
}
/* Trix leaves the placeholder to CSS, and actiontext.css does not carry the
   rule. */
trix-editor.tf-trix[placeholder]:empty:not(:focus)::before {
  content: attr(placeholder);
  color: var(--tf-ink-3);
  pointer-events: none;
}
[data-theme="dark"] trix-editor.tf-trix { background: #201c2b; }

/* Trix draws its own toolbar. Quieted down to the app's buttons rather than
   the browser's. */
trix-toolbar .trix-button-group {
  border-color: var(--tf-line);
  border-radius: var(--tf-radius-sm);
  margin-bottom: 6px;
  background: var(--tf-shell);
}
trix-toolbar .trix-button {
  border-bottom: 0;
  border-color: var(--tf-line);
  background: transparent;
  color: var(--tf-ink-2);
}
trix-toolbar .trix-button:not(:disabled):hover { background: var(--tf-hover, var(--tf-line-soft)); }
trix-toolbar .trix-button.trix-active {
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press, var(--tf-violet));
}
trix-toolbar .trix-button--icon::before { opacity: 0.75; }
[data-theme="dark"] trix-toolbar .trix-button--icon::before { filter: invert(1); opacity: 0.7; }
trix-toolbar .trix-dialog {
  background: var(--tf-shell);
  border-color: var(--tf-line);
  box-shadow: var(--tf-shadow-2);
}
trix-toolbar .trix-input--dialog {
  border-radius: var(--tf-radius-sm);
  border: 1px solid var(--tf-line);
  background: var(--tf-paper, #fff);
  color: var(--tf-ink);
}

/* An image being uploaded, and the same image once it is. Both are in the
   text where they were pasted; only the bar over the top says which. */
trix-editor .attachment--preview img,
.trix-content .attachment--preview img {
  max-width: 100%;
  height: auto;
  border-radius: var(--tf-radius-sm);
  border: 1px solid var(--tf-line);
}
trix-editor .attachment__progress {
  border-radius: 2px;
  background: var(--tf-violet-tint);
}
trix-editor .attachment__progress[value]::-webkit-progress-value { background: var(--tf-violet); }
trix-editor .attachment__progress[value]::-moz-progress-bar { background: var(--tf-violet); }

/* A saved description, wherever it is read. */
.trix-content { color: var(--tf-ink); }
.trix-content :last-child { margin-bottom: 0; }
.trix-content img { max-width: 100%; height: auto; }
.trix-content figure.attachment { margin: 8px 0; }
.trix-content figcaption.attachment__caption {
  font-size: 0.7857rem;
  color: var(--tf-ink-3);
  padding-top: 3px;
}
.trix-content a { color: var(--tf-violet-press, var(--tf-violet)); }
.trix-content blockquote {
  border-left: 3px solid var(--tf-line);
  padding-left: 12px;
  margin-left: 0;
  color: var(--tf-ink-2);
}
.trix-content pre {
  background: var(--tf-shell);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-sm);
  padding: 10px 12px;
  overflow-x: auto;
  white-space: pre-wrap;
}
/* Descriptions written before this was rich text kept their markdown images
   as plain img tags. */
.tf-prose .trix-content img { margin: 6px 0; border-radius: var(--tf-radius-sm); border: 1px solid var(--tf-line); }

/* -------------------------------------------------------------- www ----- */

.tf-www {
  background: #061525;
  color: #d7e4f2;
  min-height: 100vh;
}

.www {
  --www-navy: #061525;
  --www-deep: #0a2344;
  --www-foam: #f3f7fb;
  --www-signal: #3b9bff;
  --www-display: "Sora", var(--tf-ui);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.www-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.1rem 6vw;
}

.www-brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: #f3f7fb;
  text-decoration: none;
  font-family: var(--www-display);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.www-brand img {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  object-fit: cover;
  object-position: 50% 32%;
}

.www-nav-links { display: flex; align-items: center; gap: 0.75rem; }
.www-nav-cta {
  color: #061525;
  background: #3b9bff;
  text-decoration: none;
  font-weight: 600;
  padding: 0.45rem 0.85rem;
  border-radius: 999px;
}
.www-nav-cta:hover { color: #061525; filter: brightness(1.06); }

.www-hero {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 3vw;
  align-items: center;
  padding: 3.5rem 6vw 2rem;
}

.www-kicker {
  font-family: var(--tf-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #3b9bff;
  margin-bottom: 0.85rem;
}

.www-hero h1 {
  font-family: var(--www-display);
  font-size: clamp(2.4rem, 5vw, 4.1rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.04em;
  color: #f3f7fb;
  margin: 0 0 1rem;
}

.www-lede {
  font-size: 1.15rem;
  line-height: 1.55;
  color: #b7c9dc;
  max-width: 36rem;
  margin-bottom: 1.6rem;
}

.www-hero-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }

.www-btn-primary {
  display: inline-flex;
  align-items: center;
  background: #3b9bff;
  color: #061525;
  font-weight: 700;
  text-decoration: none;
  padding: 0.7rem 1.15rem;
  border-radius: 999px;
}
.www-btn-primary:hover { color: #061525; filter: brightness(1.06); }

.www-btn-ghost {
  display: inline-flex;
  align-items: center;
  color: #f3f7fb;
  text-decoration: none;
  font-weight: 600;
  padding: 0.7rem 1.15rem;
  border-radius: 999px;
  border: 1px solid rgba(243, 247, 251, 0.28);
}
.www-btn-ghost:hover { color: #fff; background: rgba(255, 255, 255, 0.06); }

.www-hero-art {
  justify-self: end;
  width: min(100%, 420px);
}

.www-hero-art img {
  width: 100%;
  border-radius: 28px;
  display: block;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.35);
}

.www-foot {
  display: flex;
  margin-top: auto;
  padding: 1.2rem 6vw 1.6rem;
  color: #8aa0b8;
  font-size: 0.9rem;
}

@media (max-width: 900px) {
  .www-hero { grid-template-columns: 1fr; }
  .www-hero-art { justify-self: start; width: min(100%, 320px); }
}

/* ------------------------------------------------------- notifications --- */

.tf-bell { position: relative; }
.tf-bell::after { display: none; }
.tf-bell-badge {
  position: absolute;
  top: -1px;
  right: -1px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--tf-red);
  color: #fff;
  font-size: 0.6429rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--tf-canvas);
}

.tf-note-menu { width: 22rem; padding: 0; overflow: hidden; }
.tf-note-menu-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--tf-line);
  font-size: 0.8571rem;
  font-weight: 600;
}
.tf-note-clear {
  margin-left: auto;
  border: 0;
  background: none;
  color: var(--tf-violet-hover);
  font-size: 0.8571rem;
  font-weight: 500;
  padding: 0;
}
.tf-note-clear:hover { color: var(--tf-violet-press); text-decoration: underline; }
.tf-note-permission {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-bottom: 1px solid var(--tf-line);
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press);
  font-size: 0.8571rem;
  font-weight: 500;
  text-align: left;
}
.tf-note-permission:hover { background: var(--tf-violet-tint-2); }
.tf-note-menu .tf-note-list { max-height: 22rem; overflow-y: auto; }
.tf-note-form { margin: 0; }
.tf-note {
  display: flex;
  gap: 9px;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  border-bottom: 1px solid var(--tf-line-soft);
  background: none;
  color: var(--tf-ink);
  text-align: left;
  text-decoration: none;
  transition: background var(--tf-ease);
}
.tf-note:hover { background: var(--tf-hover); color: var(--tf-ink); text-decoration: none; }
.tf-note.is-unread { background: var(--tf-violet-tint); }
.tf-note.is-unread:hover { background: var(--tf-violet-tint); }
.tf-note-ico { color: var(--tf-violet-hover); padding-top: 2px; }
.tf-note-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tf-note-title { font-size: 0.8571rem; font-weight: 500; }
.tf-note-text {
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-note-meta { font-size: 0.7857rem; color: var(--tf-ink-3); }
.tf-note-empty { padding: 22px 12px; margin: 0; text-align: center; color: var(--tf-ink-3); font-size: 0.8571rem; }
.tf-note-foot {
  display: block;
  padding: 9px 12px;
  border-top: 1px solid var(--tf-line);
  font-size: 0.8571rem;
  font-weight: 500;
  text-align: center;
}
.tf-note-foot:hover { background: var(--tf-hover); text-decoration: none; }
.tf-toast-live { background: #2f2a3d; }

/* Mentions and agents */
.tf-mention {
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press);
  border-radius: 4px;
  padding: 0 3px;
  font-weight: 500;
}
.tf-mention-menu {
  position: absolute;
  z-index: 20;
  left: 8px;
  bottom: calc(100% + 4px);
  min-width: 12rem;
  padding: 4px;
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  box-shadow: var(--tf-shadow-2);
}
.tf-mention-option {
  display: block;
  width: 100%;
  padding: 5px 8px;
  border: 0;
  border-radius: var(--tf-radius-sm);
  background: none;
  color: var(--tf-ink);
  font-size: 0.9286rem;
  text-align: left;
}
.tf-mention-option:hover, .tf-mention-option.is-active { background: var(--tf-violet-tint); color: var(--tf-violet-press); }
.tf-agent-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 18px;
  padding: 0 6px 0 4px;
  border-radius: 999px;
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press);
  font-size: 0.7143rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.tf-avatar-agent {
  background: linear-gradient(150deg, #8a76ff 10%, #5b45d6 90%);
  border-radius: var(--tf-radius-sm);
}

/* =========================================================================
   Themes. Mixed (default) pairs dark navigation with a paper work surface.
   Light keeps the whole shell pale. Dark takes the plum all the way through.
   ========================================================================= */

[data-theme="light"] {
  --tf-nav: #fafafb;
  --tf-nav-hover: #f0f1f4;
  --tf-nav-line: #e8e9ee;
  --tf-nav-fg: #40474f;
  --tf-nav-fg-dim: #8f97a3;
  --tf-nav-fg-strong: #292d34;
  --tf-nav-active: var(--tf-violet-tint);
  --tf-nav-field: #ffffff;
  --tf-nav-field-focus: #ffffff;
  --tf-nav-menu: #ffffff;
}
[data-theme="light"] .tf-side-link.is-active { color: var(--tf-violet-press); }
[data-theme="light"] .tf-side-link.is-active .tf-icon { color: var(--tf-violet-hover); }
[data-theme="light"] .tf-stage { border-left: 1px solid var(--tf-line); }

[data-theme="dark"] {
  color-scheme: dark;

  --tf-canvas: #17151f;
  --tf-shell: #1c1927;
  --tf-hover: #241f30;
  --tf-row-hover: #1e1b28;
  --tf-line: #2c2739;
  --tf-line-soft: #241f31;
  --tf-ink: #ece9f6;
  --tf-ink-2: #a9a2be;
  --tf-ink-3: #7f7896;

  --tf-nav: #201c2b;
  --tf-nav-hover: rgba(255, 255, 255, 0.06);
  --tf-nav-line: rgba(255, 255, 255, 0.09);
  --tf-nav-menu: #2a2537;

  --tf-chip-base: #221e2d;
  --tf-chip-ink: #ffffff;

  --tf-violet-tint: rgba(123, 104, 238, 0.18);
  --tf-violet-tint-2: rgba(123, 104, 238, 0.30);
  --tf-violet-hover: #a695ff;
  --tf-violet-press: #bcaeff;
  --tf-red-tint: rgba(229, 72, 77, 0.14);

  --tf-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.32);
  --tf-shadow-2: 0 6px 18px rgba(0, 0, 0, 0.42);
  --tf-shadow-3: 0 18px 44px rgba(0, 0, 0, 0.55);

  --bs-body-bg: var(--tf-canvas);
  --bs-body-color: var(--tf-ink);
  --bs-secondary-bg: var(--tf-shell);
  --bs-tertiary-bg: var(--tf-hover);
  --bs-emphasis-color: var(--tf-ink);
  --bs-border-color: var(--tf-line);
  --bs-link-color-rgb: 166, 149, 255;
  --bs-link-hover-color-rgb: 188, 174, 255;
}
[data-theme="dark"] body { background: var(--tf-canvas); }
[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select {
  background-color: #201c2b;
  border-color: var(--tf-line);
  color: var(--tf-ink);
}
[data-theme="dark"] .form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23a9a2be' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
[data-theme="dark"] .form-control::placeholder { color: var(--tf-ink-3); }
[data-theme="dark"] .form-check-input { background-color: #201c2b; border-color: #3a3450; }
[data-theme="dark"] .dropdown-menu {
  --bs-dropdown-bg: #241f31;
  --bs-dropdown-color: var(--tf-ink);
  --bs-dropdown-border-color: var(--tf-line);
  --bs-dropdown-link-color: var(--tf-ink);
  --bs-dropdown-link-hover-bg: var(--tf-hover);
  --bs-dropdown-link-hover-color: var(--tf-ink);
}
[data-theme="dark"] .btn-outline-secondary,
[data-theme="dark"] .tf-btn-quiet,
[data-theme="dark"] .tf-chip {
  --bs-btn-bg: #201c2b;
  --bs-btn-color: var(--tf-ink);
  --bs-btn-hover-bg: var(--tf-hover);
  --bs-btn-hover-color: var(--tf-ink);
  background: #201c2b;
  color: var(--tf-ink);
  border-color: var(--tf-line);
}
[data-theme="dark"] .tf-chip:hover { background: var(--tf-hover); color: var(--tf-ink); }
[data-theme="dark"] .alert-success { background: rgba(46, 171, 106, 0.14); color: #7ddda8; border-color: rgba(46, 171, 106, 0.3); }
[data-theme="dark"] .alert-danger { background: var(--tf-red-tint); color: #ff9ea1; border-color: rgba(229, 72, 77, 0.32); }
[data-theme="dark"] .alert-warning { background: rgba(217, 140, 20, 0.14); color: #eec27a; border-color: rgba(217, 140, 20, 0.3); }
[data-theme="dark"] .tf-priority-high { color: #eec27a; }
[data-theme="dark"] .tf-due.is-soon { color: #eec27a; }
[data-theme="dark"] .tf-auth {
  background:
    radial-gradient(1200px 520px at 12% -8%, #241f36 0%, rgba(0, 0, 0, 0) 62%),
    radial-gradient(900px 460px at 96% 108%, #1d2340 0%, rgba(0, 0, 0, 0) 60%),
    #14121b;
}
[data-theme="dark"] .tf-prose code { background: var(--tf-violet-tint); color: var(--tf-violet-press); }
[data-theme="dark"] .tf-assignees-more { background: var(--tf-hover); color: var(--tf-ink-2); }
[data-theme="dark"] .tf-drag { color: #4c4560; }

/* -------------------------------------------------- assignee popover ---- */

.tf-picker {
  position: fixed;
  z-index: 45;
  width: 15rem;
  background: var(--tf-canvas);
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  box-shadow: var(--tf-shadow-2);
  overflow: hidden;
  animation: tf-rise 120ms cubic-bezier(0.2, 0, 0.2, 1);
}
.tf-picker-head {
  padding: 8px 10px;
  border-bottom: 1px solid var(--tf-line);
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--tf-ink-3);
}
.tf-picker-search { padding: 8px 8px 4px; }
.tf-picker-search input { width: 100%; height: 28px; font-size: 0.8571rem; border-radius: var(--tf-radius-sm); border: 1px solid var(--tf-line); padding: 0 8px; background: var(--tf-canvas); color: var(--tf-ink); }
.tf-picker-list { max-height: 13rem; overflow-y: auto; padding: 4px; }

/* ------------------------------------------------------------- batches --- */

/* A batch is watched more than it is read: what state it is in, whether it is
   waiting on somebody, and who has it, all before a word of the title. */
.tf-batch-filters { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.tf-filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--tf-line);
  border-radius: 999px;
  background: var(--tf-canvas);
  color: var(--tf-ink-2);
  font-size: 0.8571rem;
  text-decoration: none;
}
.tf-filter-pill:hover { border-color: var(--tf-violet); color: var(--tf-violet-hover); }
.tf-filter-pill.is-on { background: var(--tf-violet-tint); border-color: var(--tf-violet); color: var(--tf-violet-press); }

.tf-batch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); gap: 14px; }
.tf-batch-card .tf-card-head h2 { font-size: 0.9286rem; }
.tf-batch-card.is-blocked { border-color: rgba(217, 140, 20, 0.5); }
.tf-batch-facts { display: flex; flex-wrap: wrap; gap: 12px; font-size: 0.8571rem; color: var(--tf-ink-2); }
.tf-batch-facts span { display: inline-flex; align-items: center; gap: 5px; }
.tf-batch-asking { color: var(--tf-amber); font-weight: 600; }
.tf-batch-holder { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 0.8571rem; color: var(--tf-ink-2); }
.tf-batch-note {
  margin: 10px 0 0;
  padding: 7px 9px;
  border-radius: var(--tf-radius-sm);
  background: var(--tf-shell);
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
}
.tf-batch-send { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--tf-line); }
.tf-card.is-blocked { border-color: rgba(217, 140, 20, 0.5); }

/* A question an agent is waiting on. Open, it is the loudest thing on the
   page, because the whole batch has stopped behind it. */
.tf-bq { padding: 12px 0; border-bottom: 1px solid var(--tf-line-soft); }
.tf-bq:last-child { border-bottom: 0; padding-bottom: 0; }
.tf-bq.is-open { border-left: 2px solid var(--tf-amber); padding-left: 12px; margin-left: -14px; }
.tf-bq-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tf-bq-body { margin: 4px 0 8px; color: var(--tf-ink); }
.tf-bq-body p:last-child { margin-bottom: 0; }
.tf-bq-answer {
  padding: 9px 11px;
  border-radius: var(--tf-radius);
  background: var(--tf-shell);
  border: 1px solid var(--tf-line);
  font-size: 0.9286rem;
}
.tf-bq-answer p:last-child { margin-bottom: 0; }
.tf-bq-answer-head {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 0.7857rem;
  font-weight: 600;
  color: var(--tf-green);
  margin-bottom: 4px;
}
.tf-bq-form { margin-top: 4px; }
.tf-picker-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 7px;
  border-radius: var(--tf-radius-sm);
  font-size: 0.9286rem;
  cursor: pointer;
}
.tf-picker-option:hover { background: var(--tf-hover); }
.tf-picker-empty { padding: 10px 8px; margin: 0; color: var(--tf-ink-3); font-size: 0.8571rem; }
.tf-picker-foot {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  padding: 8px;
  border-top: 1px solid var(--tf-line);
}
.tf-cell-edit.is-picking { box-shadow: inset 0 0 0 2px var(--tf-violet); border-radius: 4px; }

/* Theme picker on the profile screen */
.tf-theme-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.tf-theme-option { position: relative; }
.tf-theme-option input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.tf-theme-card {
  display: block;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  overflow: hidden;
  cursor: pointer;
  transition: border-color var(--tf-ease), box-shadow var(--tf-ease);
}
.tf-theme-option input:checked + .tf-theme-card {
  border-color: var(--tf-violet);
  box-shadow: 0 0 0 3px var(--tf-violet-tint);
}
.tf-theme-option input:focus-visible + .tf-theme-card { outline: 2px solid var(--tf-violet); outline-offset: 2px; }
.tf-theme-preview { display: flex; height: 54px; }
.tf-theme-rail { width: 12px; }
.tf-theme-side { width: 34%; }
.tf-theme-body { flex: 1; }
.tf-theme-name { padding: 7px 9px; font-size: 0.8571rem; font-weight: 500; border-top: 1px solid var(--tf-line); }
.tf-theme-mixed .tf-theme-rail { background: #17121f; }
.tf-theme-mixed .tf-theme-side { background: #221b2e; }
.tf-theme-mixed .tf-theme-body { background: #ffffff; }
.tf-theme-light .tf-theme-rail { background: #17121f; }
.tf-theme-light .tf-theme-side { background: #fafafb; }
.tf-theme-light .tf-theme-body { background: #ffffff; }
.tf-theme-dark .tf-theme-rail { background: #17121f; }
.tf-theme-dark .tf-theme-side { background: #201c2b; }
.tf-theme-dark .tf-theme-body { background: #17151f; }

/* ----------------------------------------------------------------- tags --- */

.tf-tag-row { display: inline-flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.tf-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 9rem;
  height: 20px;
  padding: 0 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 14%, var(--tf-chip-base));
  color: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 74%, var(--tf-chip-ink));
  font-size: 0.7857rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tf-tag-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.tf-tag-more { background: var(--tf-line-soft); color: var(--tf-ink-2); }
.tf-tag-x {
  display: inline-grid;
  place-items: center;
  width: 14px;
  height: 14px;
  border: 0;
  background: none;
  color: inherit;
  opacity: 0.55;
  padding: 0;
  line-height: 0;
}
.tf-tag-x:hover { opacity: 1; }
.tf-tag-form { display: inline; margin: 0; }
.tf-tag-add { display: flex; gap: 6px; align-items: center; margin-top: 6px; }
.tf-tag-add input { height: 28px; font-size: 0.8571rem; max-width: 11rem; }
.tf-picker-create {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 6px 8px;
  border: 0;
  border-top: 1px solid var(--tf-line);
  background: none;
  color: var(--tf-violet-hover);
  font-size: 0.8571rem;
  font-weight: 500;
  text-align: left;
}
.tf-picker-create:hover { background: var(--tf-violet-tint); }

/* Notification rows: what it is about, then who did what. */
.tf-note { align-items: flex-start; gap: 10px; }
.tf-note-face { padding-top: 1px; }
.tf-note-body { flex: 1; min-width: 0; }
.tf-note-title,
.tf-note-meta,
.tf-note-text {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-note-title { font-weight: 600; }
.tf-note-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  margin-top: 7px;
  border-radius: 999px;
  background: var(--tf-violet);
}

/* Inline token editing */
.tf-token-edit-row > td { padding: 0 !important; background: var(--tf-shell); }
.tf-token-edit { padding: 14px 16px; border-top: 1px solid var(--tf-line); }
.tf-token-picture { display: flex; align-items: flex-start; gap: 12px; }
.tf-token-picture-now { flex: 0 0 auto; }
.tf-token-picture .tf-avatar { box-shadow: 0 0 0 2px var(--tf-canvas); }
.tf-picker-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* =========================================================================
   List rows, second pass: the task cell carries status and type, selection
   is visible before you hover, and the bulk bar reads as plain actions.
   ========================================================================= */

/* Rail: icon and label both mark the active area, and stay legible whether
   the active pill is dark (mixed, dark) or near-white (light). */
.tf-rail-item.is-active span { color: #fff; font-weight: 600; }
.tf-rail-item:hover span { color: #fff; }
[data-theme="light"] .tf-rail-item.is-active span,
[data-theme="light"] .tf-rail-item.is-active .tf-icon { color: var(--tf-violet-press); }
[data-theme="light"] .tf-rail-item.is-active:hover span,
[data-theme="light"] .tf-rail-item.is-active:hover .tf-icon { color: var(--tf-violet-press); }

/* Status as a dot in front of the task. The ring is always the status colour
   and always solid; what changes is the pie inside it. Work that has not
   started is a washed-out disc, each in-progress status fills an even step
   further round, and a completed status floods the whole circle edge to edge
   — so "how far along is this" reads down the column without reading a word.
   Both numbers arrive as custom properties: --tf-c the colour, --tf-fill the
   turn of the pie. */
.tf-status-dot {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  padding: 1.5px;
  border: 2px solid var(--tf-c, var(--tf-ink-3));
  border-radius: 999px;
  background-color: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 22%, transparent);
  background-image: conic-gradient(var(--tf-c, var(--tf-ink-3)) calc(var(--tf-fill, 0) * 1turn), transparent 0);
  /* Clipped to the padding box, which leaves a hairline of canvas between the
     ring and the pie. Without it a part-filled circle reads as a smudge. */
  background-clip: content-box;
  background-origin: content-box;
  background-repeat: no-repeat;
  transition: box-shadow var(--tf-ease), background-color var(--tf-ease), border-color var(--tf-ease);
}
/* Nothing has happened yet: outline only, and the little colour there is
   drains away so a backlog stays quiet next to work in flight. */
.tf-status-dot.is-not-started {
  background-color: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 14%, transparent);
  background-image: none;
}
.tf-status-dot.is-complete {
  background-color: var(--tf-c, var(--tf-ink-3));
  background-image: none;
  background-clip: border-box;
  background-origin: border-box;
}
button.tf-status-dot { cursor: pointer; }
/* Hover lifts a ring around the dot rather than filling it: the fill is a
   fact about the task, and pointing at it does not move the work on. */
button.tf-status-dot:hover {
  background-color: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 38%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 22%, transparent);
}
button.tf-status-dot.is-complete:hover { background-color: var(--tf-c, var(--tf-ink-3)); }
.tf-status-dot.is-picking { box-shadow: 0 0 0 3px color-mix(in srgb, var(--tf-c) 30%, transparent); }
.tf-cell-title .tf-type { gap: 0; }
.tf-cell-title .tf-type i { font-size: 0.9286rem; }

/* Assignees: people together, agents apart. */
.tf-assign-cell { display: flex; align-items: center; gap: 10px; width: 100%; }
.tf-assign-agents { display: inline-flex; align-items: center; gap: 2px; margin-left: auto; }
.tf-assign-agents .tf-avatar { box-shadow: 0 0 0 2px var(--tf-canvas); }

/* Selection is visible before you reach for it. */
.tf-row .form-check-input {
  opacity: 1;
  transition: border-color var(--tf-ease), background-color var(--tf-ease);
}
/* Only the empty box is outlined — a ticked one keeps its violet fill. */
.tf-row .form-check-input:not(:checked) { border-color: #c3c7d0; background-color: transparent; }
.tf-row:hover .form-check-input:not(:checked) { border-color: var(--tf-violet); }
[data-theme="dark"] .tf-row .form-check-input:not(:checked) { border-color: #4a4460; }
.tf-row { cursor: pointer; }
.tf-row .tf-cell-edit, .tf-row a, .tf-row button, .tf-row input, .tf-row select { cursor: auto; }
.tf-row a, .tf-row button.tf-status-dot, .tf-row .tf-drag { cursor: pointer; }

/* Status picker */
.tf-picker-slim { width: 12.5rem; }
/* The picker shows the same value the cell will show, so the pills match the
   cell: full width of the row they sit in rather than hugging their text. A
   list of colours you are choosing between reads as a list of colours. */
.tf-picker-slim .tf-picker-pick .tf-status {
  display: block;
  width: 100%;
  height: 24px;
  line-height: 24px;
  padding: 0 9px;
  border-radius: 5px;
  overflow: hidden;
  text-overflow: ellipsis;
  background: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 22%, var(--tf-chip-base));
}
.tf-picker-pick {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  border: 0;
  background: none;
  color: var(--tf-ink);
  text-align: left;
}
.tf-picker-pick.is-current { background: var(--tf-violet-tint); color: var(--tf-violet-press); font-weight: 600; }

/* Bulk bar */
.tf-bulk-dock { bottom: 46px; }
.tf-bulk {
  gap: 0;
  padding: 6px 8px;
  border-radius: 12px;
  background: #1c1e24;
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.tf-bulk-count {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  margin-right: 6px;
  padding: 0 5px 0 9px;
  border-radius: 8px;
  background: #33363f;
  font-size: 0.8571rem;
  font-weight: 600;
  white-space: nowrap;
}
.tf-bulk-x {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: #b8bcc6;
}
.tf-bulk-x:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
.tf-bulk-act {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  height: 28px;
  padding: 0 7px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: #d7dae1;
  font-size: 0.8571rem;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--tf-ease), color var(--tf-ease);
}
.tf-bulk-act:hover { background: rgba(255, 255, 255, 0.10); color: #fff; }
.tf-bulk-act .tf-icon { color: #a2a7b3; }
.tf-bulk-act:hover .tf-icon { color: #fff; }
.tf-bulk-act.is-danger .tf-icon { color: #ff8f92; }
.tf-bulk-act.is-danger:hover { background: rgba(229, 72, 77, 0.18); }
.tf-bulk-act.is-danger:hover .tf-icon { color: #ff8f92; }
/* The native select sits invisibly over its label, so one click opens it. */
.tf-bulk-act select {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  border: 0;
  cursor: pointer;
}
.tf-bulk-sep { width: 1px; height: 18px; margin: 0 5px; background: rgba(255, 255, 255, 0.14); }


/* =========================================================================
   Detail pane properties, the task page's editable properties, and tags
   that stay on one line.
   ========================================================================= */

/* Two properties to a row: a task's shape is one glance, not a long ladder. */
.tf-meta-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 16px;
  padding: 6px 0 16px;
  border-bottom: 1px solid var(--tf-line);
  margin-bottom: 16px;
}
.tf-field { min-width: 0; }
.tf-field.is-wide { grid-column: 1 / -1; }
.tf-field-key {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--tf-ink-3);
  margin-bottom: 5px;
}
.tf-field-key .tf-icon { color: var(--tf-ink-3); }
.tf-field-val { min-width: 0; }
.tf-field-val .form-select,
.tf-field-val .form-control { height: 32px; width: 100%; max-width: none; padding-top: 0; padding-bottom: 0; }
.tf-field-val .tf-chip { max-width: 100%; }
.tf-field-val .tf-tag-add input { max-width: none; flex: 1; }

/* The task page's Properties card carries the same click-to-edit cells the
   board does, so they need room to be clicked. */
.tf-meta-grid-flat { border-bottom: 0; margin-bottom: 0; padding: 0; }
.tf-meta-grid .tf-meta-val.tf-cell-edit {
  padding: 3px 6px;
  margin: -3px -6px;
  border-radius: 5px;
  min-height: 28px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}
.tf-meta-grid .tf-meta-val.tf-cell-edit:hover { box-shadow: inset 0 0 0 1px var(--tf-line); background: var(--tf-hover); }

/* Tags ride at the end of the task's title rather than in a column of their
   own. They keep to the row's one line, and they never take space from the
   title itself — the title truncates first, because a task you cannot read
   the name of is worse than a tag you cannot read the name of. */
.tf-tag-row.is-one-line { flex-wrap: nowrap; }
.tf-title-tags {
  display: inline-flex;
  align-items: center;
  flex: 0 1 auto;
  flex-wrap: nowrap;
  gap: 4px;
  min-width: 0;
  max-width: 14rem;
  overflow: hidden;
  padding: 2px 4px;
  margin: -2px -4px;
  border-radius: 5px;
}
.tf-title-tags .tf-tag { max-width: 6.5rem; }
.tf-title-tags.tf-cell-edit:hover { background: var(--tf-hover); box-shadow: inset 0 0 0 1px var(--tf-line); }
/* An untagged row has nothing to show and would have nothing to click, so it
   keeps a mark that only surfaces under the pointer. */
.tf-tag-hint { display: inline-grid; place-items: center; color: var(--tf-ink-3); opacity: 0; transition: opacity var(--tf-ease); }
.tf-row:hover .tf-tag-hint, .tf-title-tags:hover .tf-tag-hint { opacity: 1; }
.tf-title-tags:has(.tf-tag-hint) { padding-inline: 2px; margin-inline: -2px; }
.tf-tag-x {
  flex: 0 0 auto;
  margin-right: -2px;
  border-radius: 999px;
}
.tf-tag-x:hover { background: color-mix(in srgb, var(--tf-c, var(--tf-ink-3)) 22%, transparent); }
.tf-row .tf-tag-x { cursor: pointer; }

/* Leaving full screen: back to the row this task came from. */
.tf-minimise { margin-left: 4px; align-self: center; }
.tf-minimise:hover { background: var(--tf-hover); color: var(--tf-ink); }

/* ------------------------------------------------- dropdown field values --- */

.tf-cf-options { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.tf-cf-option { display: flex; align-items: center; gap: 6px; }
.tf-cf-option input[type="color"] { flex: 0 0 auto; width: 34px; padding: 3px; }
.tf-cf-option input[type="text"] { flex: 1 1 6rem; min-width: 0; }
.tf-cf-preview { flex: 0 0 auto; max-width: 8rem; overflow: hidden; text-overflow: ellipsis; }
.tf-cf-add { padding-left: 2px; }
.tf-cf-add svg { margin-right: 2px; vertical-align: -2px; }

/* ------------------------------------------------------- storage badges --- */

/* Whose bucket a workspace's files land in. BYOS is the exception, so it is
   the one that carries colour — a platform workspace should read as quiet. */
.tf-store-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  background: var(--tf-hover);
  color: var(--tf-ink-2);
  box-shadow: inset 0 0 0 1px var(--tf-line);
}
.tf-store-badge.is-byos {
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press);
  box-shadow: inset 0 0 0 1px var(--tf-violet-tint-2);
}
.tf-store-badge .tf-icon { color: currentColor; }
.tf-store-note { font-size: 0.7857rem; color: var(--tf-ink-3); white-space: nowrap; }

/* =========================================================================
   Live work: the timeline a task keeps, who is typing into it, and the two
   pages that read across a workspace — one person's own work wherever it
   lives, and everything that has happened here.
   ========================================================================= */

/* ------------------------------------------------------------ timeline --- */

/* Comments and what happened share one column. A comment is the loud thing on
   it and keeps the weight it always had; an activity line is deliberately
   quieter — it is context for the conversation, not part of it. */
.tf-timeline { display: flex; flex-direction: column; }
.tf-timeline-empty { margin: 6px 0 0; }
.tf-tl-entry { display: flex; gap: 10px; padding: 9px 0; }
.tf-tl-comment + .tf-tl-comment,
.tf-tl-act + .tf-tl-comment,
.tf-tl-comment + .tf-tl-act { border-top: 1px solid var(--tf-line-soft); }

.tf-tl-act { align-items: flex-start; padding: 5px 0; }
.tf-tl-mark {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--tf-hover);
  color: var(--tf-ink-3);
}
.tf-tl-mark.is-agent { background: var(--tf-violet-tint); color: var(--tf-violet-hover); }
[data-theme="dark"] .tf-tl-mark.is-agent { background: rgba(123, 104, 238, 0.22); color: var(--tf-violet-light); }

.tf-tl-line {
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 5px;
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
  line-height: 22px;
}
.tf-tl-who { font-weight: 600; color: var(--tf-ink); }
.tf-tl-val { font-weight: 600; color: var(--tf-ink); font-style: normal; }
a.tf-tl-val, .tf-tl-line a { color: var(--tf-violet-hover); font-weight: 600; }
.tf-tl-when, .tf-tl-dim { font-size: 0.7857rem; color: var(--tf-ink-3); }
.tf-tl-when { margin-left: 2px; }

/* Which door a change came in through. A person clicking in the web UI is the
   unremarkable case and says nothing; an agent or a webhook is worth a word. */
.tf-tl-src {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 0 5px;
  height: 17px;
  border-radius: 999px;
  border: 1px solid var(--tf-line);
  background: var(--tf-shell);
  font-size: 0.7143rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--tf-ink-2);
}
.tf-tl-src.is-agent {
  border-color: var(--tf-violet-tint-2);
  background: var(--tf-violet-tint);
  color: var(--tf-violet-press);
}
[data-theme="dark"] .tf-tl-src.is-agent {
  border-color: rgba(123, 104, 238, 0.4);
  background: rgba(123, 104, 238, 0.18);
  color: var(--tf-violet-light);
}
.tf-tl-via { font-size: 0.7143rem; color: var(--tf-ink-3); }

/* -------------------------------------------------------------- typing --- */

/* Only true while it is on screen. Nothing about it is stored. */
.tf-typing {
  display: flex;
  align-items: center;
  gap: 7px;
  min-height: 22px;
  padding: 2px 2px 6px;
  font-size: 0.7857rem;
  color: var(--tf-ink-3);
}
.tf-typing[hidden] { display: none; }
.tf-typing-dots { display: inline-flex; gap: 3px; }
.tf-typing-dots i {
  width: 4px;
  height: 4px;
  border-radius: 999px;
  background: var(--tf-violet);
  animation: tf-typing 1.1s infinite ease-in-out;
}
.tf-typing-dots i:nth-child(2) { animation-delay: 0.15s; }
.tf-typing-dots i:nth-child(3) { animation-delay: 0.3s; }
@keyframes tf-typing {
  0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) {
  .tf-typing-dots i { animation: none; opacity: 0.6; }
}

/* ---------------------------------------------------------- board news --- */

/* A task somebody else added. Where it belongs on your board depends on how
   you have grouped and filtered it, so it is named rather than slotted in. */
.tf-board-news:empty { display: none; }
.tf-board-news { padding: 6px 16px 0; display: flex; flex-direction: column; gap: 4px; }
.tf-board-news-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  border: 1px dashed var(--tf-line);
  border-radius: var(--tf-radius);
  background: var(--tf-shell);
  font-size: 0.8571rem;
  color: var(--tf-ink-2);
}
.tf-board-news-go {
  margin-left: auto;
  border: 0;
  background: none;
  padding: 0;
  font-size: 0.8571rem;
  font-weight: 600;
  color: var(--tf-violet-hover);
  cursor: pointer;
}
.tf-board-news-go:hover { text-decoration: underline; }

/* A row somebody else added, dropped straight into the group it belongs to.
   Nobody asked for it, so it lands lit and fades back into the board rather
   than appearing as though it had been there all along. */
.tf-row.is-arriving { animation: tf-arrive 1.4s ease-out; }
@keyframes tf-arrive {
  from { background-color: color-mix(in srgb, var(--tf-violet) 16%, transparent); }
  to { background-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .tf-row.is-arriving { animation-duration: 1ms; }
}

/* ------------------------------------------------------------- my work --- */

.tf-mywork { padding-bottom: 40px; }
.tf-mywork .tf-toolbar { border-bottom: 1px solid var(--tf-line); }

/* One card per workspace, so "how much is waiting over there" is answered
   before you have to go and look. */
.tf-ws-strip {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  padding: 12px 16px 14px;
  border-bottom: 1px solid var(--tf-line-soft);
}
.tf-ws-card {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 168px;
  padding: 8px 12px 8px 9px;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  background: var(--tf-canvas);
  color: var(--tf-ink);
  text-decoration: none;
  transition: border-color var(--tf-ease), box-shadow var(--tf-ease);
}
.tf-ws-card:hover { text-decoration: none; border-color: var(--tf-violet-tint-2); box-shadow: var(--tf-shadow-1); }
.tf-ws-card.is-on { border-color: var(--tf-violet); box-shadow: 0 0 0 1px var(--tf-violet); }
.tf-ws-card.is-quiet { opacity: 0.62; }
.tf-ws-card-mark { flex: 0 0 auto; }
.tf-ws-card-all { display: grid; place-items: center; width: 26px; height: 26px; border-radius: var(--tf-radius-sm); background: var(--tf-ink); color: #fff; }
.tf-ws-card-body { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.tf-ws-card-name { font-weight: 600; font-size: 0.8571rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tf-ws-card-count { font-size: 0.7857rem; color: var(--tf-ink-3); font-variant-numeric: tabular-nums; }

.tf-mywork-groups { padding: 4px 0 0; }
.tf-mywork-group + .tf-mywork-group { margin-top: 6px; }
.tf-mywork-group-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px 7px;
  background: var(--tf-canvas);
}
.tf-mywork-group-head h2 { font-size: 0.8571rem; margin: 0; letter-spacing: 0.01em; }
.tf-mywork-list { list-style: none; margin: 0; padding: 0; }

.tf-mywork-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--tf-row-h);
  padding: 4px 16px;
  border-top: 1px solid var(--tf-line-soft);
}
.tf-mywork-row:hover { background: var(--tf-row-hover); }
.tf-mywork-row.is-done .tf-mywork-name { color: var(--tf-ink-3); text-decoration: line-through; }
.tf-mywork-name {
  min-width: 0;
  font-size: 0.9286rem;
  color: var(--tf-ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-mywork-name:hover { color: var(--tf-violet-press); text-decoration: none; }
.tf-mywork-side { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.tf-mywork-git { display: inline-flex; align-items: center; gap: 3px; font-size: 0.7857rem; color: var(--tf-ink-3); }

/* Which workspace a line came from, kept to the width of a mark plus a word:
   on this page it is the one column that is never the same twice. */
.tf-mywork-ws { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.tf-mywork-ws-name {
  font-size: 0.7857rem;
  color: var(--tf-ink-2);
  max-width: 10rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-mywork-empty { padding: 34px 16px; }

@media (max-width: 900px) {
  .tf-mywork-side { gap: 6px; }
  .tf-mywork-ws-name, .tf-mywork-row .tf-tag, .tf-mywork-git { display: none; }
}

/* --------------------------------------------------------------- feed --- */

.tf-feed-day + .tf-feed-day { margin-top: 18px; }
.tf-feed-date {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 0.7857rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--tf-ink-2);
  padding-bottom: 6px;
}
.tf-feed {
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
  background: var(--tf-canvas);
  overflow: hidden;
}
.tf-feed-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 12px; }
.tf-feed-row + .tf-feed-row { border-top: 1px solid var(--tf-line-soft); }
.tf-feed-row:hover { background: var(--tf-row-hover); }
.tf-feed-body { min-width: 0; flex: 1; }
.tf-feed-where { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.tf-feed-task {
  font-size: 0.8571rem;
  font-weight: 500;
  color: var(--tf-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tf-feed-task:hover { color: var(--tf-violet-press); }
.tf-feed-when {
  flex: 0 0 auto;
  font-size: 0.7857rem;
  color: var(--tf-ink-3);
  font-variant-numeric: tabular-nums;
  padding-top: 2px;
}

/* ---------------------------------------------------------- statuses page --

   The statuses list is a settings table you can drag, so its handle behaves
   the way the board's does: out of the way until the row is under the mouse. */
.tf-status-row:hover .tf-drag { opacity: 1; }
.tf-status-cell { display: flex; align-items: center; gap: 9px; }

/* What a status means for the work in it, said in a word next to the circle
   that says it in a shape. */
.tf-status-kind {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border: 1px solid var(--tf-line);
  border-radius: 999px;
  font-size: 0.7857rem;
  font-weight: 500;
  color: var(--tf-ink-2);
  white-space: nowrap;
}
.tf-status-kind.is-not-started { color: var(--tf-ink-3); }
.tf-status-kind.is-in-progress { color: var(--tf-blue); border-color: color-mix(in srgb, var(--tf-blue) 30%, var(--tf-line)); }
.tf-status-kind.is-completed { color: var(--tf-green); border-color: color-mix(in srgb, var(--tf-green) 30%, var(--tf-line)); }

.tf-copy-sources { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.tf-copy-sources li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius);
}

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

   One dialog per page, filled by a turbo-frame. Padding sits on the parts
   inside rather than the dialog itself, so a click anywhere the content is
   not is a click on the backdrop, and closes it. */
.tf-dialog {
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(44rem, calc(100vh - 3rem));
  padding: 0;
  border: 1px solid var(--tf-line);
  border-radius: var(--tf-radius-lg);
  background: var(--tf-canvas);
  color: var(--tf-ink);
  box-shadow: var(--tf-shadow-2);
  overflow: hidden;
}
.tf-dialog::backdrop { background: rgba(20, 18, 28, 0.42); }
/* Only when open: a bare `display: flex` would beat the UA rule that keeps a
   closed dialog off the screen. */
.tf-dialog[open] { display: flex; flex-direction: column; }

/* The head and the foot are fixed; the body between them takes what is left
   and scrolls. The chain has to run unbroken from the dialog down to the
   body, because a link in it that sizes to its content — the turbo-frame, or
   the form wrapping the body and the foot — hands the body an unbounded
   height, and the confirm button ends up below the bottom edge. `min-height:
   0` at each step is what lets a flex child be shorter than its content. */
.tf-dialog[open] > turbo-frame,
.tf-dialog[open] > turbo-frame > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
.tf-dialog-head, .tf-dialog-foot { flex: 0 0 auto; }
.tf-dialog-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--tf-line);
}
.tf-dialog-head h2 { margin: 0; font-size: 1.0714rem; font-weight: 600; }
.tf-dialog-head .tf-icon-btn { margin-left: auto; }
.tf-dialog-sub { margin: 3px 0 0; font-size: 0.8571rem; color: var(--tf-ink-3); }
.tf-dialog-body { flex: 1 1 auto; min-height: 0; padding: 18px; overflow-y: auto; overscroll-behavior: contain; }
.tf-dialog-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 13px 18px;
  border-top: 1px solid var(--tf-line);
  background: var(--tf-shell);
}

.tf-copy-block + .tf-copy-block { margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--tf-line-soft); }
.tf-copy-title { margin: 0 0 8px; font-size: 0.7857rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--tf-ink-3); }
.tf-copy-list, .tf-copy-map { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.tf-copy-list li { display: flex; align-items: center; gap: 9px; }
.tf-copy-note { margin-left: auto; font-size: 0.8571rem; }
.tf-copy-tag { padding: 2px 8px; border-radius: 999px; font-size: 0.7857rem; font-weight: 500; }
.tf-copy-tag.is-new { color: var(--tf-green); background: color-mix(in srgb, var(--tf-green) 12%, transparent); }
.tf-copy-tag.is-changed { color: var(--tf-amber); background: color-mix(in srgb, var(--tf-amber) 14%, transparent); }

/* Where the tasks on a status about to disappear are being sent. */
.tf-copy-map li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 13rem);
  align-items: center;
  gap: 10px;
}
.tf-copy-from { display: flex; align-items: center; gap: 9px; min-width: 0; }
.tf-copy-arrow { color: var(--tf-ink-3); display: inline-grid; place-items: center; }
@media (max-width: 34rem) {
  .tf-copy-map li { grid-template-columns: minmax(0, 1fr); }
  .tf-copy-arrow { display: none; }
}
