/* tools — internal tools site chrome (served at /tools/static/css/tools.css).
   Intentionally small; the heavy styling comes from /chrome/base.css (core).
   Only the brand mark, the door/landing, and the tools list are styled here. */

.tools-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  background: #2b2b2b;
  color: #6fd3ff;
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.5px;
  text-decoration: none;
}
.tools-logo-item a { text-decoration: none; }

.tools-page {
  max-width: 720px;
  margin: 32px auto 64px;
  padding: 0 20px;
  line-height: 1.6;
}
.tools-page h1 {
  font-size: 28px;
  margin: 0 0 12px;
}
.tools-muted {
  color: #9aa0a6;
}

.tools-tools {
  list-style: none;
  padding: 0;
  margin: 24px 0;
  display: grid;
  gap: 12px;
}
.tools-tools li {
  background: #1f1f1f;
  border: 1px solid #2c2c2c;
  border-radius: 8px;
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.tools-tools a {
  color: #6fd3ff;
  font-weight: 600;
  text-decoration: none;
  font-size: 17px;
}
.tools-tools a:hover { text-decoration: underline; }
.tools-tools-desc {
  color: #9aa0a6;
  font-size: 14px;
}

.tools-table {
  width: 100%;
  border-collapse: collapse;
  margin: 18px 0;
  font-size: 13px;
}
.tools-table th, .tools-table td {
  border-bottom: 1px solid #2c2c2c;
  padding: 6px 10px;
  text-align: left;
  white-space: nowrap;
}
.tools-table th {
  color: #9aa0a6;
  font-weight: 600;
}
.tools-table .tools-heat-hi { background: #3a1f1f; color: #ff9d9d; font-weight: 700; }
.tools-table .tools-heat-mid { background: #332a1a; color: #ffd58a; font-weight: 600; }

/* ── dashboard (per-target cards) ─────────────────────────────────────────
   Each target is a card split into four labelled bands (link / app / box
   pressure / pg). Metric tiles sit in an auto-fill grid, so the cards reflow
   from a single column on a phone to a wider multi-tile row on desktop —
   no horizontal scroll, JS-free. A coloured left edge gives a quick-glance
   health cue: green = healthy, amber = high pressure, red = down. */
.tools-wide { max-width: 1040px; }
.tools-hint { font-size: 12.5px; color: #8a8f94; margin: 8px 0 0; }

.tools-cards { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.tools-card {
  background: #1b1d20; border: 1px solid #2a2d31; border-radius: 12px;
  overflow: hidden; min-width: 0; border-left: 3px solid #2f6d86;
}
.tools-card.is-warn { border-left-color: #d7a13a; }
.tools-card.is-down { border-left-color: #d75050; }

.tools-card-head {
  padding: 12px 14px; border-bottom: 1px solid #24272b;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
}
.tools-card-id { min-width: 0; }
.tools-card-name { font-size: 17px; font-weight: 700; color: #eef0f2; }
.tools-card-sub { font-size: 12px; color: #8a8f94; margin-top: 2px; word-break: break-all; }
.tools-card-badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

.tools-sec { padding: 11px 14px 13px; }
.tools-sec + .tools-sec { border-top: 1px solid #212429; }
.tools-sec-label {
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  margin: 0 0 8px; display: inline-flex; align-items: center; gap: 6px;
}
.tools-dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

/* expand toggle for the collapsed detail surface (box pressure + pg).
   <details>/<summary> gives free, JS-free collapse; this just styles the
   summary bar to read like a section label with a live state hint. */
.tools-card-more { border-top: 1px solid #212429; }
.tools-card-more summary {
  cursor: pointer; user-select: none; list-style: none;
  padding: 9px 14px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: #8a8f94;
}
.tools-card-more summary::-webkit-details-marker { display: none; }
.tools-card-more summary::marker { content: ""; }
.tools-more-label { display: inline-flex; align-items: center; gap: 6px; }
.tools-more-caret { margin-left: auto; font-weight: 600; color: #5f666d; letter-spacing: .04em; }
.tools-card-more:not([open]) .tools-more-caret::before { content: "expand ▸ "; }
.tools-card-more[open] .tools-more-caret::before        { content: "collapse ▾ "; }


.tools-tiles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 7px;
}
.tools-tile {
  background: #202327; border: 1px solid #2c3035; border-radius: 9px;
  padding: 7px 10px; min-width: 0;
}
.tools-tile .k {
  font-size: 10.5px; color: #8a8f94; display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tools-tile .v {
  font-size: 16px; font-weight: 600; margin-top: 3px; color: #e9ebee;
  font-variant-numeric: tabular-nums; display: block;
}
.tools-tile .v small { font-size: 11px; font-weight: 500; color: #8a8f94; margin-left: 1px; }

/* heat: tint the whole tile so a saturated resource jumps out */
.tools-tile.hi  { background: #2c1a1a; border-color: #5a2a2a; }
.tools-tile.hi .v  { color: #ff9d9d; }
.tools-tile.mid { background: #2a231a; border-color: #544326; }
.tools-tile.mid .v { color: #ffd58a; }

/* boolean / value tone */
.tools-tile .v.ok   { color: #8fe08a; }
.tools-tile .v.bad  { color: #ff9d9d; }
.tools-tile .v.miss { color: #6b7075; }

/* section + legend accent colors */
.c-link { color: #a4e08a; } .d-link { background: #4a9e3f; }
.c-app  { color: #c9a4ff; } .d-app  { background: #8058c9; }
.c-box  { color: #ffce7a; } .d-box  { background: #d7a13a; }
.c-pg   { color: #63d1c8; } .d-pg   { background: #2f9e93; }

/* header status pills */
.tools-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 11px; font-size: 11px; font-weight: 600;
}
.tools-pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.tools-pill-ok      { background: #17301c; color: #8fe08a; }
.tools-pill-bad     { background: #391d1d; color: #ff9d9d; }
.tools-pill-restart { background: #3a3120; color: #ffd58a; }
.tools-pill-restart::before { content: "▲"; background: none; }

/* card footer — WHEN this target's data was last collected */
.tools-collected {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; color: #8a8f94; white-space: nowrap;
}
.tools-collected::before { content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: #3f4348; display: inline-block; }
.tools-collected.fresh::before { background: #5fd38a; }
.tools-collected.stale::before { background: #ffd58a; }
.tools-collected.stale { color: #ffd58a; }
.tools-card-foot {
  padding: 8px 14px; border-top: 1px solid #212429;
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; font-size: 11px; color: #8a8f94;
}
.tools-down-stamp { color: #ff9d9d; font-weight: 600; }

/* footer legend */
.tools-legend { margin-top: 16px; font-size: 12.5px; color: #8a8f94; }
.tools-legend .lg {
  display: inline-flex; align-items: center; gap: 6px; margin: 4px 16px 0 0; white-space: nowrap;
}

/* narrow screens: tighter tiles + type */
@media (max-width: 520px) {
  .tools-tiles { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 6px; }
  .tools-tile .v { font-size: 15px; }
  .tools-card-name { font-size: 16px; }
}

/* ── notes (collaborative note + todo) ─────────────────────────────────── */
/*
   Palette (dark, matches the rest of the tools site):
     surface #1a1a1a (body)   panel #1c1e21   panel-2 #1e2024
     border  #2a2d31          border-soft #24272b
     ink     #e6e8ea          ink-dim #9aa0a6   muted #6b7076
     accent  #6fd3ff

   base.css (core) ships global `input[type=text]`, `button`, `p`, and
   `h1,h2,h3{text-align:center}` rules that cascade into here and, on a couple
   of selectors, OUTRANK our class rules on specificity. Rather than chase
   them rule-by-rule, we first reset the form controls + headings inside
   `.note-page` at higher specificity, then the component rules below refine.
*/
.note-page { max-width: 760px; }

/* global-control reset (see header comment). Specificity (0,1,1) ties/outranks
   base.css's `input[type="text"]`/`button` and, being last, wins any tie. */
.note-page input, .note-page select, .note-page textarea {
  width: auto; box-sizing: border-box; margin: 0;
  background: #1a1c1f; border: 1px solid #2a2d31; color: #e6e8ea;
  font-family: inherit; font-size: 15px; padding: 9px 11px; border-radius: 9px;
}
.note-page input::placeholder { color: #5a5f66; }
.note-page button { width: auto; box-sizing: border-box; }
.note-page h1, .note-page h2, .note-page h3 { text-align: left; }

/* ── shared bits ── */
.note-muted, .note-page .tools-muted, .note-empty, .note-saved { color: #9aa0a6; font-size: 14px; }
.note-page .tools-muted { margin: 12px 0 0; line-height: 1.55; }
.note-page .note-empty { margin: 16px 0 0; color: #6b7076; }
.note-saved { min-height: 1.2em; font-size: 13px; margin: 14px 0 0; }
.note-saved.err { color: #f0a0a0; }
.note-saved.ok  { color: #9ad2a0; }

/* lock banner: a thin strip between the head and the edit surface. Two static
   spans (me / other) + a dot + a small release button. The JS flips which span
   is visible and what the .note-lock-who text says (the holder's username, or
   "waiting for a free edit slot…" if no one is holding it). */
.note-lock {
  margin: 10px 0 14px;
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid #33363b;
  background: #1c1e21;
  display: flex; align-items: center; gap: 10px;
  font-size: 13px;
}
.note-lock .note-lock-me, .note-lock .note-lock-other {
  display: inline-flex; align-items: center; gap: 8px;
  color: #e6e8ea;
}
.note-lock .note-lock-dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: #6fd3ff; box-shadow: 0 0 0 3px rgba(111, 211, 255, .18);
}
.note-lock .note-lock-dot.wait { background: #e0b85c; box-shadow: 0 0 0 3px rgba(224, 184, 92, .18); }
.note-lock .note-lock-release {
  margin-left: auto;
  padding: 4px 12px; font-size: 12.5px; font-weight: 500;
  border-radius: 7px; border: 1px solid #3a4a55; background: #26313a; color: #cfe6f2;
  cursor: pointer;
}
.note-lock .note-lock-release:hover { background: #2d3a44; border-color: #4a6272; }
.note-lock .note-lock-release:disabled { cursor: default; opacity: .55; }
/* the blocked state (the "other" span is showing) tints the whole banner a
   hair warmer so a glance reads "not editing" — no layout shift. */
.note-lock-other .note-lock-who { color: #cfd3d8; }

/* buttons: primary (Save / share) + subtle (rest) + danger. All width:auto
   (see reset) so they never fill a flex row. */
.note-btn {
  appearance: none; border: 1px solid #33363b; background: #26292e; color: #e6e8ea;
  padding: 8px 16px; border-radius: 9px; cursor: pointer;
  font-family: inherit; font-size: 14px; font-weight: 500;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.note-btn:hover { background: #2e3237; border-color: #41505c; }
.note-btn:active { background: #22252a; }
/* Save + Share read as the primary action. */
.note-btn.note-primary { background: #2f6d86; border-color: #2f6d86; color: #fff; }
.note-btn.note-primary:hover { background: #367795; border-color: #367795; }
.note-btn.note-danger { border-color: #5a3434; color: #e9a9a9; background: transparent; }
.note-btn.note-danger:hover { background: #3a2424; border-color: #7a4444; }

/* ── index ── */
.note-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.note-head h1 { margin: 0; flex: 0 0 auto; font-size: 26px; font-weight: 700; color: #eef0f2; text-align: left; }
.note-spacer { flex: 1 1 auto; }

.note-list { list-style: none; margin: 0; padding: 6px; border: 1px solid #2a2d31; border-radius: 12px; background: #1c1e21; }
.note-row { display: flex; align-items: center; gap: 14px; padding: 14px 16px; min-width: 0; }
.note-row + .note-row { border-top: 1px solid #24272b; }
.note-row-title {
  color: #eef0f2; text-decoration: none; font-weight: 600; font-size: 15px;
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.note-row-title:hover { color: #6fd3ff; }
.note-meta { color: #8a8f94; font-size: 13px; margin-left: auto; white-space: nowrap; display: flex; align-items: center; gap: 10px; }
.badge { font-size: 11px; padding: 2px 9px; border-radius: 999px; background: #2a2d31; color: #c8ccd1; font-weight: 600; }
.badge.note-you { background: #1f3a45; color: #8fd8ff; }

/* ── single note ── */
/* Fixed chrome links: pin the colour in EVERY anchor state — the browser's
   default :link/:visited colours (visited purple) bleed through on some
   engines and these links should read as UI, not hyperlinks. The
   state-qualified variants carry the extra specificity, so the bare class
   alone would not guarantee it. Same treatment below for .kb-back,
   .kb-row-title and .kb-open-note. */
.note-back, .note-back:link, .note-back:visited { color: #9ad2ff; text-decoration: none; font-size: 14px; flex: 0 0 auto; }
.note-back:hover { color: #6fd3ff; }
input.note-title {
  flex: 1 1 260px; min-width: 200px;
  background: transparent; border-color: transparent; color: #f2f4f6;
  font-size: 22px; font-weight: 700; padding: 8px 10px; line-height: 1.2;
}
input.note-title:focus { background: #1a1c1f; border-color: #334049; }

/* toolbar: small squarish icon buttons (B I U • 1.) + a compact link field +
   Link button — all width:auto (the global `button{width:100%}` is killed by
   the .note-page reset, so each stays its glyph size). */
.note-toolbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px;
  border: 1px solid #2a2d31; border-bottom: 0; border-radius: 10px 10px 0 0;
  padding: 8px 10px; background: #1e2024;
}
.note-toolbar button {
  appearance: none; border: 1px solid transparent; background: transparent; color: #c8ccd1;
  min-width: 32px; height: 32px; padding: 0 9px; border-radius: 7px; cursor: pointer;
  font-family: inherit; font-size: 14px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.note-toolbar button:hover { background: #2a2d31; color: #fff; border-color: #383d44; }
.note-tb-sep { width: 1px; align-self: stretch; background: #33363b; margin: 2px 4px; }
input.note-link {
  flex: 1 1 140px; min-width: 90px; margin: 0;
  background: #141619; border-color: #2a2d31; color: #d3d6da;
  padding: 6px 10px; font-size: 13px;
}
input.note-link:focus { outline: none; border-color: #334049; }

/* editor */
.note-editor {
  border: 1px solid #2a2d31; border-radius: 0 0 10px 10px; background: #1a1c1f;
  padding: 18px 20px; min-height: 280px; max-height: 70vh; overflow-y: auto;
  color: #e6e8ea; line-height: 1.7; font-size: 15px;
}
.note-editor:focus { outline: none; border-color: #334049; }
.note-editor p { margin: 0.6em 0; }
.note-editor h1 { font-size: 1.6em; margin: 0.5em 0; line-height: 1.3; font-weight: 700; }
.note-editor h2 { font-size: 1.35em; margin: 0.5em 0; font-weight: 700; }
.note-editor h3 { font-size: 1.15em; margin: 0.5em 0; font-weight: 600; }
.note-editor a { color: #6fd3ff; text-decoration: underline; }
.note-editor ul, .note-editor ol { padding-left: 1.5em; margin: 0.6em 0; }
.note-editor li { margin: 0.28em 0; }
.note-editor b, .note-editor strong { font-weight: 700; }

/* todo items */
/* row = [check] [text flex:1] [✕]. Independent flex siblings (no <label> wrap)
   so clicking the text edits it and the ✕ stays an easy hit target. */
.note-items { list-style: none; margin: 0; padding: 6px 4px; border: 1px solid #2a2d31; border-radius: 10px; background: #1c1e21; }
.note-item { display: flex; align-items: center; gap: 12px; padding: 9px 14px; min-width: 0; }
.note-item + .note-item { border-top: 1px solid #24272b; }
.note-item-check { width: 18px; height: 18px; cursor: pointer; accent-color: #6fd3ff; flex: 0 0 auto; }
.note-item-text {
  flex: 1 1 auto; padding: 4px 6px; border-radius: 6px; min-height: 1.6em;
  color: #e6e8ea; min-width: 0;
}
.note-item-text:focus { outline: none; background: #141619; }
.note-item-done .note-item-text { color: #7a7f86; text-decoration: line-through; }
.note-item-del {
  appearance: none; border: 0; background: transparent; color: #6b7076; cursor: pointer;
  font-size: 15px; padding: 4px 8px; border-radius: 6px; flex: 0 0 auto; line-height: 1;
}
.note-item-del:hover { background: #3a2424; color: #f0b4b4; }

.note-item-add { display: flex; gap: 10px; margin-top: 12px; }
.note-item-add input {
  flex: 1 1 auto; background: #1a1c1f; border: 1px solid #2a2d31; color: #e6e8ea;
  padding: 10px 12px; font-size: 14px;
}
.note-item-add input:focus { outline: none; border-color: #334049; }
/* Save + Delete row (todo only) — below the "add item" box, Delete pushed right. */
.note-item-actions { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }
.note-item-actions .note-danger { margin-left: auto; }

/* share panel (owner only) — a folded card, not a legacy <details> grey box */
.note-share { margin-top: 22px; border: 1px solid #2a2d31; border-radius: 12px; background: #1b1d20; overflow: hidden; }
.note-share-sum {
  list-style: none; cursor: pointer; padding: 15px 18px; font-weight: 600; color: #e6e8ea;
  display: flex; align-items: center; gap: 4px; font-size: 15px;
}
.note-share-sum::-webkit-details-marker { display: none; }
.note-share[open] .note-share-sum { border-bottom: 1px solid #24272b; }
.note-share-tip { color: #8a8f94; font-weight: 400; font-size: 13px; }
.note-share-body { padding: 16px 18px; }
.note-share form { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.note-share select {
  flex: 1 1 180px; background: #141619; border: 1px solid #2a2d31; color: #d3d6da;
  padding: 10px 12px; font-size: 14px;
}
.note-share-list { list-style: none; margin: 16px 0 0; padding: 0; }
.note-share-list li { display: flex; align-items: center; gap: 10px; padding: 10px 0; color: #c8ccd1; }
.note-share-list li + li { border-top: 1px solid #24272b; }

/* narrow */
@media (max-width: 520px) {
  .note-head { gap: 10px; }
  input.note-title { flex-basis: 100%; }
  .note-item-add { flex-direction: column; }
  .note-item-actions .note-danger { margin-left: 0; }
}

/* ── /tools/tokens ──────────────────────────────────────────────────── */
.tk-card {
  margin: 14px 0; padding: 0; border: 1px solid #2a2d31; border-radius: 12px;
  background: #1b1d20; overflow: hidden;
}
.tk-card summary {
  cursor: pointer; padding: 14px 18px; font-size: 15px; color: #e6e8ea;
  display: flex; align-items: center; gap: 4px;
}
.tk-card summary::-webkit-details-marker { display: none; }
.tk-card[open] summary { border-bottom: 1px solid #24272b; }
.tk-card form { padding: 16px 18px; display: flex; flex-direction: column; gap: 14px; }
.tk-card form label { display: flex; flex-direction: column; gap: 6px; color: #c8ccd1; font-size: 14px; }
.tk-card form label.tk-scope { flex-direction: row; align-items: center; gap: 8px; cursor: pointer; }
.tk-card form label.tk-scope input[type="checkbox"] { width: 15px; height: 15px; flex-shrink: 0; }
.tk-card form label.tk-scope code { cursor: help; }
.tk-card form label input[type="text"] {
  background: #141619; border: 1px solid #2a2d31; color: #d3d6da; padding: 10px 12px; font-size: 14px;
}
.tk-card form button[type="submit"] {
  align-self: flex-start; background: #1f6feb; color: #fff; border: 0; padding: 10px 16px;
  font-size: 14px; cursor: pointer; border-radius: 8px;
}
.tk-card form button[type="submit"]:hover { background: #2680ff; }
.tk-table { width: 100%; border-collapse: collapse; font-size: 13px; color: #c8ccd1; }
.tk-table th, .tk-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #24272b; }
.tk-table th { color: #8a8f94; font-weight: 600; text-transform: none; }
.tk-table code { color: #6e9fd6; }
.tk-muted { color: #6a6e73; font-size: 12px; }
.tk-secret-card {
  margin: 16px 0; padding: 18px 22px; border: 1px solid #304a75; border-radius: 12px;
  background: #12151b;
}
.tk-secret-label { color: #e6e8ea; font-size: 14px; display: block; margin-bottom: 12px; }
.tk-secret-wrap { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.tk-secret {
  flex: 1 1 400px; background: #0d0f13; border: 1px solid #1f6feb; color: #8fc7ff;
  padding: 12px 14px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 15px; user-select: all; border-radius: 6px;
}
.tk-copy {
  background: #1f6feb; color: #fff; border: 0; padding: 10px 14px; font-size: 13px;
  cursor: pointer; border-radius: 6px;
}
.tk-copy:hover { background: #2680ff; }
.tk-card pre {
  margin: 0; padding: 14px 16px; background: #0d0f13; border: 1px solid #24272b;
  border-radius: 8px; font-size: 12px; line-height: 1.5; color: #c8ccd1; overflow-x: auto;
}
.tk-card code { color: #8fc7ff; }

/* ── kanban (/tools/kanban) ──────────────────────────────────────────── */
/* Reset base.css global controls inside the kanban pages (board included).
   Same technique as .note-page: higher specificity + last-in-cascade wins. */
/* Body is a COLUMN flex with align-start sizing, so an auto-width item like
   this page box resolves to its max-CONTENT width (the min-width floor is
   irrelevant — the cross size takes max-content, not min, unless an
   explicit max-width clamps it). Consequence found by measurement: a
   40-char assignee name in the task form's <select> (≈442px) inflated the
   WHOLE page box to 516px at a 390px viewport and h-overflowed the
   document. min(1100px, 100vw) keeps the 1100px wide cap where it applies
   and clamps the box to the viewport when the viewport is narrower, so
   the fields inside then bound at their row's width (.kb-in's
   max-width:100%, the chips' wrap) instead of setting it. */
.kb-page, .kb-task-page { max-width: min(1100px, 100vw); }
/* border-box: with the content-box model the page's 20px horizontal padding
   (`.tools-page`) sits OUTSIDE the cap — measured 430px border box at a
   390px viewport. A page wider than its viewport is what makes mobile
   Safari/Chrome zoom the whole page out to fit it (measured: the 303-
   landing task page laying out at 444px wide with clientWidth 390 — the
   +54px h-overflow the doc check flags), so the padding must count toward
   the cap. The page box then fills the viewport exactly (390 border-box,
   350 content) instead of overhanging it by 40px. */
.kb-page, .kb-task-page { box-sizing: border-box; }
/* The page box shrinks to wrap its content: body's flex + .tools-page's auto
   margins already make the page's WIDTH fit its content, and this makes each
   card fit its fields (as wide as the widest field, not stretched to the
   page width). The board page intentionally keeps its stretched grid.
   `min-width: 0`: the page box is a FLEX ITEM of body's row flex, where
   min-width:auto = the page's min-content — and a wide unbreakable field
   (the 40-char assignee in the <select> ≈ 442px, measured) sets that floor,
   so at 390px viewport the page refused to shrink below ~516px and h-
   overflowed the document. min-width:0 lets it shrink to the viewport, and
   the fields inside then bound at min(max-content, row-width) — the same
   flex-item overflow class .kb-board already defuses with min-width:0. */
.kb-page, .kb-task-page { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
.kb-page input, .kb-page select, .kb-page textarea,
.kb-task-page input, .kb-task-page select, .kb-task-page textarea,
.kb-board input, .kb-board select, .kb-board textarea {
  width: auto; box-sizing: border-box; margin: 0;
  background: #1a1c1f; border: 1px solid #2a2d31; color: #e6e8ea;
  font-family: inherit; font-size: 14px; padding: 8px 10px; border-radius: 8px;
}
.kb-page input::placeholder, .kb-task-page input::placeholder,
.kb-board input::placeholder,
.kb-page textarea::placeholder, .kb-task-page textarea::placeholder,
.kb-board textarea::placeholder { color: #5a5f66; }
.kb-page button, .kb-task-page button, .kb-board button { width: auto; box-sizing: border-box; }
.kb-page h1, .kb-page h2, .kb-page h3,
.kb-task-page h1, .kb-task-page h2, .kb-task-page h3,
.kb-board h1, .kb-board h2, .kb-board h3 {
  text-align: left;
  /* overflow-wrap: anywhere — a pasted unbreakable title (a 90-char string
     with no spaces) has no legal break, so at 390px its max-content width
     (~1400px) set the page width and the whole document h-overflowed on a
     phone (mobile browsers then zoom out to fit it). `anywhere` lets even
     that wrap. The other kb text already wraps (see .kb-card-title). */
  overflow-wrap: anywhere;
}

/* index: org list */
.kb-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.kb-head h1 { margin: 0; flex: 0 0 auto; font-size: 26px; font-weight: 700; color: #eef0f2; }
.kb-empty { margin: 0 0 18px; }
.kb-list { list-style: none; margin: 0; padding: 6px; border: 1px solid #2a2d31; border-radius: 12px; background: #1c1e21; }
.kb-row { display: flex; align-items: center; gap: 14px; padding: 14px 16px; min-width: 0; }
.kb-row + .kb-row { border-top: 1px solid #24272b; }
.kb-row-title, .kb-row-title:link, .kb-row-title:visited { color: #eef0f2; text-decoration: none; font-weight: 600; font-size: 15px; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-row-title:hover { color: #6fd3ff; }

/* create org */
.kb-create { margin-bottom: 18px; border: 1px solid #2a2d31; border-radius: 12px; background: #1b1d20; }
.kb-create summary { cursor: pointer; padding: 13px 16px; font-weight: 600; font-size: 15px; color: #e6e8ea; }
.kb-create summary::-webkit-details-marker { display: none; }
.kb-create[open] summary { border-bottom: 1px solid #24272b; }
.kb-create .kb-form { padding: 14px 16px; }
/* The box wraps the name field + Create (the page's flex-start fit above).
   `.kb-in`'s `flex: 1 1 180px` must be neutralized in this column form —
   its flex-basis sizes the input's HEIGHT there (the 180px-tall name box:
   the class of bug `.kb-inline` defuses on the board's horizontal forms) —
   and the field gets a comfortable width to type in. */
.kb-create .kb-form .kb-in { flex: 0 0 auto; height: auto; min-width: 220px; }

/* board shared */
.kb-flash { min-height: 1.2em; font-size: 13px; margin: 8px 0 0; }
.kb-flash.err { color: #f0a0a0; }
.kb-flash.ok  { color: #9ad2a0; }
.kb-back, .kb-back:link, .kb-back:visited { color: #9ad2ff; text-decoration: none; font-size: 14px; }
.kb-back:hover { color: #6fd3ff; }
.kb-sec-h { font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; margin: 24px 0 10px; color: #8a8f94; }

/* buttons */
.kb-btn {
  appearance: none; border: 1px solid #33363b; background: #26292e; color: #e6e8ea;
  padding: 7px 14px; border-radius: 8px; cursor: pointer;
  font-family: inherit; font-size: 13.5px; font-weight: 500;
  transition: background .12s ease, border-color .12s ease;
}
.kb-btn:hover { background: #2e3237; border-color: #41505c; }
.kb-btn:active { background: #22252a; }
.kb-btn.kb-primary { background: #2f6d86; border-color: #2f6d86; color: #fff; }
.kb-btn.kb-primary:hover { background: #367795; }
.kb-btn.kb-danger { border-color: #5a3434; color: #e9a9a9; background: transparent; }
.kb-btn.kb-danger:hover { background: #3a2424; border-color: #7a4444; }
.kb-btn.kb-mini { padding: 4px 10px; min-height: 32px; font-size: 12px; border-radius: 6px; min-width: 28px; }

/* form inputs */
.kb-in { flex: 1 1 180px; min-width: 120px; }
.kb-in-narrow { flex: 0 1 120px; min-width: 100px; }
.kb-in-widest { flex: 1 1 100%; }
/* `.kb-in-num`: the small step/offset number boxes on the recurring
   schedule rows (`.kb-in`'s 120px floor would swallow the inline label). */
.kb-in-num { flex: 0 1 76px; min-width: 56px; }
/* `.kb-in-wide`: the title/description fields of the recurring form — full
   width of their label column (`.kb-in`'s 180px basis would truncate them). */
.kb-in-wide { flex: 1 1 100%; min-width: 0; }
.kb-form { display: flex; flex-direction: column; gap: 10px; }
/* Compact single-row form (org rename, add member/state/project).
   Ordering requirement: this rule MUST come after `.kb-form` above — both
   selectors have equal specificity, and `.kb-form` sets
   `flex-direction: column`. If `.kb-inline` ever loses that cascade fight,
   the `.kb-in*` flex-basis values start sizing input HEIGHTS instead of
   widths (the 142px-tall rename box). */
.kb-inline { display: flex; flex-direction: row; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }

/* board head: identity (back link, name, role badge). The org actions
   (rename / transfer / delete) live in the Manage panel below the board —
   `.kb-head-actions` remains for the task page's save/delete cluster, where
   it eats the leftover flex space so the controls hug the right edge. Every
   control is content-sized (see the `.kb-board` reset above); nothing may
   stretch. */
.kb-board-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.kb-board-head h1 { margin: 0; font-size: 22px; font-weight: 700; color: #eef0f2; }
.kb-head-id { display: inline-flex; align-items: center; gap: 12px; min-width: 0; }
.kb-head-actions { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.kb-head-actions form { margin: 0; }
.kb-divider { width: 1px; align-self: stretch; background: #33363b; margin: 0 4px; }

/* Board frame: must fit the viewport at ANY width. Core's `body
   { display: flex }` gives .kb-board shrink-to-fit sizing, so without an
   explicit bound it settles at its size-preferred width (1040px content-box
   + 40px padding) and overhangs the viewport from ~700px wide up to ~1100px;
   body's `overflow-x: hidden` then hides the overhang instead of scrolling
   it — state columns on the right would be clipped and unreachable.
   Constrain the frame to 100% of the viewport (border-box, so `.tools-page`'s
   20px horizontal padding is part of it); when a viewport can't fit the
   columns, the `.kb-grid` scrolls internally instead (see its rule). */
.kb-board { box-sizing: border-box; max-width: 100%; min-width: 0; overflow-x: auto; }

/* role badges */
.kb-badge { font-size: 11px; padding: 2px 9px; border-radius: 999px; background: #2a2d31; color: #c8ccd1; font-weight: 600; }
.kb-badge.kb-role-owner  { background: #2a2010; color: #ffd58a; }
.kb-badge.kb-role-admin  { background: #1f3a45; color: #8fd8ff; }
.kb-badge.kb-role-member { background: #2a2d31; color: #9aa0a6; }

/* manage panel */
.kb-manage { margin: 16px 0; border: 1px solid #2a2d31; border-radius: 12px; background: #1b1d20; }
.kb-manage summary { cursor: pointer; padding: 12px 16px; font-weight: 600; font-size: 14px; color: #e6e8ea; }
.kb-manage summary::-webkit-details-marker { display: none; }
.kb-manage[open] summary { border-bottom: 1px solid #24272b; }
/* Admin/owner grid has four columns (org / members / states / projects); the
   member's solo grid below overrides it to one. */
.kb-manage-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; padding: 12px 0; }
/* A plain member manages only the projects column: one column, no dividers. */
.kb-manage-grid.kb-manage-solo { grid-template-columns: 1fr; }
.kb-manage-col { padding: 0 16px; }
.kb-manage-col + .kb-manage-col { border-left: 1px solid #24272b; }
.kb-manage-col h3 { font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; margin: 0 0 8px; color: #8a8f94; }

.kb-member-list, .kb-state-list, .kb-project-list { list-style: none; margin: 0 0 10px; padding: 0; }
.kb-member, .kb-state, .kb-project {
  display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 13.5px; color: #c8ccd1;
}
.kb-member + .kb-member, .kb-state + .kb-state, .kb-project + .kb-project { border-top: 1px solid #24272b; }
.kb-member-name, .kb-state-name, .kb-project-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-member-tools { display: inline-flex; gap: 4px; margin-left: auto; }

/* recurring panel (below Manage): same card grammar as .kb-manage. NOTE:
   `.kb-recur-sched` is used for TWO things — the one-line schedule text on a
   template row (a <span> inside .kb-recur-line) and the schedule-fields
   container (.kb-recur-fields.kb-recur-sched). Never style it bare; scope to
   the row (.kb-recur-line …) or the fields container respectively. */
.kb-recur { margin: 16px 0; border: 1px solid #2a2d31; border-radius: 12px; background: #1b1d20; }
/* Keep the UA's disclosure caret (▸ closed / ▾ open, auto-flipped by state):
   parity with `.kb-manage summary` (the Manage panel directly above this one
   deliberately keeps it). Do NOT add `list-style: none` here — that was the
   one declaration that suppressed the caret. (The per-row `.kb-recur-editbox
   summary` below does carry one: that summary is an "Edit" button, not a
   section title.) */
.kb-recur > summary { cursor: pointer; padding: 12px 16px; font-weight: 600; font-size: 14px; color: #e6e8ea; }
.kb-recur > summary::-webkit-details-marker { display: none; }
.kb-recur[open] > summary { border-bottom: 1px solid #24272b; }
.kb-recur-hint { margin: 12px 16px 0; font-size: 13px; line-height: 1.55; }
.kb-recur-create { margin: 12px 16px; padding: 12px 14px; border: 1px solid #24272b; border-radius: 10px; background: #1c1e21; }
.kb-recur-create .kb-recur-fields + .kb-recur-fields { margin-top: 12px; }
.kb-recur-list { margin: 12px 16px 16px; display: flex; flex-direction: column; gap: 10px; }
.kb-recur-row { padding: 10px 14px; border: 1px solid #24272b; border-radius: 10px; background: #1c1e21; }
/* row line 1: title + schedule/cell summary + meta + tag chips */
.kb-recur-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.kb-recur-line .kb-recur-title { font-size: 14px; font-weight: 600; color: #eef0f2; }
.kb-recur-line .kb-recur-sched, .kb-recur-line .kb-recur-meta { font-size: 12px; }
.kb-recur-row .kb-recur-chip { margin: 0; }
/* actions row: run-now / edit / add-tag / delete */
.kb-recur-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.kb-recur-run { border-color: #2f6d86; color: #9fd9f5; }
.kb-recur-run:hover { background: #1f3a45; border-color: #367795; }
.kb-recur-tagform { margin: 0; }
/* the edit form hides behind its <summary class="kb-btn">; open, it takes a
   full wrap-line so the fields breathe */
.kb-recur-editbox { display: inline-flex; }
.kb-recur-editbox summary { cursor: pointer; list-style: none; }
.kb-recur-editbox summary::-webkit-details-marker { display: none; }
.kb-recur-editbox[open] { display: block; flex: 1 1 100%; margin-top: 4px; }
.kb-recur-editbox .kb-recur-form { margin: 10px 0 0; padding: 10px 12px; border: 1px solid #24272b; border-radius: 10px; background: #181a1d; }
.kb-recur-editbox .kb-recur-form .kb-recur-fields + .kb-recur-fields { margin-top: 10px; padding-top: 10px; border-top: 1px solid #24272b; }
/* field grids: labels are column-flex (label above the control), the
   schedule groups (mode / weekdays / steps) are inline flex rows */
.kb-recur-fields { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 16px; }
.kb-recur-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 150px; min-width: 130px; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #8a8f94; }
/* These labels are COLUMN-flex (the control sits below the label), so
   `.kb-in`'s `flex: 1 1 180px` basis ends up sizing the control's HEIGHT —
   the 180px-tall Row / Column / Assignee selects. The same class of bug
   `.kb-inline` and `.kb-create .kb-form .kb-in` defuse above: neutralize the
   basis here too. No visible change for the `kb-in-wide` title/description
   inputs (their 100% basis already resolves to auto in an auto-height
   label). */
.kb-recur-field .kb-in { flex: 0 0 auto; height: auto; max-width: 100%; }
.kb-recur-field.kb-recur-wide { flex-basis: 260px; }
.kb-recur-fields .kb-recur-mode,
.kb-recur-fields .kb-recur-wd,
.kb-recur-fields .kb-recur-step { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 12.5px; color: #c8ccd1; }
.kb-recur-fields .kb-recur-mode label,
.kb-recur-fields .kb-recur-wd label { display: inline-flex; align-items: center; gap: 4px; margin: 0; }
.kb-recur-actions-inline { margin-left: auto; }

/* the recurring schedule's disabled (mode-inactive) fields, greyed: the
   server renders disabled per the template's mode, kanban.js re-syncs on the
   mode-radio change — the CSS just makes "not used" visually obvious. */
.kb-recur-fields .kb-in:disabled,
.kb-recur-fields .kb-recur-wd label:has(input:disabled) { opacity: .5; cursor: default; }

/* the grid: CSS grid, first column is the project-name label, the rest are the
   state columns. The track list (120px + N×1fr) is inline on .kb-grid — the
   server knows N; CSS can't repeat() over a runtime count, so it lives there. */
.kb-grid {
  display: grid;
  gap: 0;
  border: 1px solid #2a2d31;
  border-radius: 12px;
  /* Keep horizontal scroll (do NOT `overflow: hidden`): card text now
     wraps (overflow-wrap: anywhere), but the 1fr columns still have a
     min-content floor (tags, assignee names, an overlong project label),
     so the grid's laid-out width can outgrow its box — `overflow: hidden`
     would quietly clip the rightmost state columns and leave them
     unreachable. Any overflow other than visible still gives us the
     radius clipping. */
  overflow-x: auto; overflow-y: hidden;
  background: #1c1e21;
}
.kb-row-label {
  grid-row: auto;
  padding: 8px 12px;
  font-size: 12.5px; font-weight: 600; color: #8a8f94;
  background: #1e2024;
  border-right: 1px solid #2a2d31;
  border-bottom: 1px solid #24272b;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.kb-row-label-corner { background: #1a1c1f; }
.kb-col-head {
  padding: 10px 12px; font-size: 12.5px; font-weight: 700; color: #c8ccd1;
  background: #1e2024; border-bottom: 1px solid #2a2d31;
}
.kb-cell {
  padding: 8px; min-height: 72px;
  border-right: 1px solid #24272b; border-bottom: 1px solid #24272b;
  display: flex; flex-direction: column; gap: 6px;
}

/* task card inside a cell */
.kb-card {
  display: block; min-width: 0;
  padding: 8px 10px; border-radius: 8px;
  background: #202327; border: 1px solid #2c3035;
  text-decoration: none; color: #e6e8ea;
  transition: background .1s ease, border-color .1s ease;
}
.kb-card:hover { background: #262a30; border-color: #3a4048; }
.kb-card[draggable="true"] { cursor: grab; }
.kb-card[draggable="true"]:active { cursor: grabbing; }
/* Card text wraps (no single-line ellipsis: clipped text hid what the card
   was about — the board's columns scroll, so the extra height is cheap).
   overflow-wrap lets even unbreakable tokens wrap, keeping the 1fr
   columns' min-content floor small enough to fit narrow viewports. */
.kb-card-title { display: block; font-size: 13.5px; font-weight: 600; color: #eef0f2; overflow-wrap: anywhere; }
.kb-card-desc { display: block; font-size: 12px; color: #8a8f94; margin-top: 3px; overflow-wrap: anywhere; }
.kb-card-tags { display: flex; gap: 4px; margin-top: 5px; flex-wrap: wrap; }
/* assignee/due: measured at 390px they fit (a 40-char name ≈ 280px < the
   ~330px card), so this is defense, not the fix — it stops a longer name
   from re-creating the min-content floor the card title/desc rules defuse. */
.kb-assignee { font-size: 11px; color: #9aa0a6; margin-top: 4px; overflow-wrap: anywhere; }
.kb-due { font-size: 11px; color: #9aa0a6; margin-top: 2px; overflow-wrap: anywhere; }
.kb-note-badge { font-size: 12px; color: #6fd3ff; margin-left: 6px; }

/* chips (small tag pills) */
.kb-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; padding: 4px 10px; border-radius: 999px;
  background: #2a2d31; color: #c8ccd1;
  /* min-width:0 + max-width:100%: the pill is a flex item of the card/tags
     row, where a flex item's min-content floor is its text. A 40-char tag
     name is unbreakable, so without the pair it pushed the 1fr column's
     min-content floor 128px past the 390 viewport (measured board
     internal-scroll). The text lives in a .kb-chip-label span (emitted by
     the server) so it can be told to wrap there. */
  min-width: 0; max-width: 100%;
}
.kb-chip-label { min-width: 0; overflow-wrap: anywhere; }
.kb-chip-lg { font-size: 13px; padding: 4px 10px; }
.kb-chip-x {
  appearance: none; border: 0; background: transparent; color: #6b7076;
  cursor: pointer; font-size: 12px; padding: 6px; line-height: 1;
}
.kb-chip-x:hover { color: #f0b4b4; }
.kb-chip-more { font-size: 11px; color: #8a8f94; }

/* add-task button in a cell */
.kb-add-task {
  appearance: none; border: 1px dashed #33363b; background: transparent; color: #8a8f94;
  padding: 8px 12px; min-height: 32px; border-radius: 6px; cursor: pointer; font-size: 13px;
  align-self: flex-start; margin-top: auto;
}
.kb-add-task:hover { background: #26292e; border-color: #4a6272; color: #6fd3ff; }

/* In-cell task-creation form: kanban.js toggles it in on the + click (the
   button stays in the cell — clicking it again closes the form, as does
   Cancel, and it yields its margin-top:auto while open). A plain native
   form (POST → the server's 302 redirect) replaces the old window.prompt()
   flow: a prompt sync-blocks the click handler and is blocked in many
   mobile/automated contexts, whereas a visible input is keyboard-first
   (Enter submits) and progressive-enhancement safe. flex-wrap because a
   narrow cell (~155px at 390 with two columns) can't hold the input and
   the two buttons on one row. */
.kb-addform { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-top: auto; }
.kb-addform .kb-actions { display: flex; gap: 6px; }

/* drag-over highlight */
.kb-drag-over { background: #1f3a45; border-color: #3f8fbf !important; }

/* ── task detail page ──
   Same grammar as the board: the head reuses the .kb-board-head two-cluster
   layout (identity + role badge left; Save / Delete hug the right edge via
   .kb-head-actions' margin-left:auto, divider between) and each section sits
   in a card with the same metrics as the board's .kb-manage panel. */
/* max-width:100%: without it the panel is its max-CONTENT width — the
   assignee <select> sizes to its LONGEST option (a 40-char name ≈ 442px,
   measured) and the whole form chain (panel → form → fields → label)
   overhangs the capped page box at narrow viewports. The page's content
   width is definite (see the kb-page cap above), so 100% resolves against
   it and the field's own max-width:100% then bounds the control to the row
   (the select clips its long value natively). Measured consequence of
   leaving it uncapped: mobile Safari/Chrome zoom the page OUT to fit the
   wide in-flow content (a 390px viewport measured 496px wide on the task
   page), which is the h-overflow the doc check was flagging. */
.kb-panel { margin: 16px 0; border: 1px solid #2a2d31; border-radius: 12px; background: #1b1d20; padding: 14px 16px; box-sizing: border-box; max-width: 100%; }
.kb-panel-h { margin: 0 0 12px; font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: #8a8f94; }
.kb-no-tags { margin: 10px 0 0; }
/* The head row itself is .kb-board-head (above); only the h1 differs (a task
   title is one level bigger than the board's org name). Keep this rule AFTER
   the .kb-board-head rules so the size override wins the cascade. */
.kb-task-top h1 { font-size: 24px; }
.kb-task-form { margin: 0; }
/* align-items flex-start so the field keeps its own width (default stretch
   would widen it to the card, defeating the tight wrap). */
.kb-task-form label { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
/* Field controls: the card wraps its fields tightly (see the flex-start fit
   on `.kb-task-page` above), so each control sits at its own width instead
   of stretching to the card. The `.kb-in*` flex shorthand still has to be
   neutralized — in a column-labelled form its flex-basis sizes the field's
   HEIGHT (the ~180px-tall boxes `.kb-inline` defuses on the board's
   horizontal forms), so: no grow/shrink, content width, natural height. */
/* max-width:100%: the assignee <select> is sized to its LONGEST option —
   a 40-char name ≈ 400px overhanging the 390px viewport (measured). Cap
   the control to its own row's width; the user scrolls the closed list. */
.kb-task-form label .kb-in { flex: 0 0 auto; width: auto; min-width: 180px; height: auto; box-sizing: border-box; max-width: 100%; }
/* Description: min-height is the floor (taller on mobile — see the narrow
   media query); kanban.js sizes the rest to the text so the box fits its
   contents (no inner scrollbars), hence `resize: none` — a manual vertical
   resize would fight the auto-fit. */
.kb-task-form textarea { min-width: 220px; min-height: 120px; resize: none; }
/* Title + Description (the `.kb-in-widest` pair) fill the panel instead of
   sitting at content width: the label's flex column has
   `align-items: flex-start` (needed so a NARROW mobile panel still wraps
   its fields tightly), which leaves an auto-width control at CONTENT
   width — a ~150px-clipped Title box even inside the full-width desktop
   panel (measured). An explicit border-box width (the `.kb-in` rule above
   already sets box-sizing) resolves the cross size to the label's — i.e.
   the panel's — content width at every viewport. Height is untouched:
   `height: auto` and the flex-basis/height interplay of `.kb-in` remain
   exactly as before. The selector matches the sibling `.kb-in` rule's shape
   (label, 0,2,1) so it beats that rule's `width: auto` on source order — a
   bare 2-class `.kb-in-widest` rule loses the cascade (measured: the Title
   box stayed at ~220px content width inside the 1060px panel). */
.kb-task-form label .kb-in-widest { width: 100%; }
/* Assignee + due stack (the card is as wide as the widest field, not the
   two side by side). */
.kb-task-fields { display: flex; flex-direction: column; gap: 10px; }
.kb-task-fields label { flex: 0 0 auto; align-items: flex-start; }
.kb-tag-add { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.kb-tags-list { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
/* Desktop fit: the page box is STRETCHED to its 1100px cap (body is a flex
   column and .tools-page's auto margins centre, not shrink, the box), but
   `.kb-task-page`'s `align-items: flex-start` leaves each child at its
   content width. On a phone that reads fine (the page box IS the phone, so
   the panels already fill it), but on a desktop the panels settle at a
   ~373px phone-width column inside the 1100px box — the whole page renders
   like a mobile layout (measured: 373px panels, a ~150px-clipped Title
   input, Save/Delete stuck mid-page instead of right-hugged). Stretch the
   head, the panels, and the checklist card to the page box so the page
   uses the desktop width; the heads then hand their leftover space to the
   `.kb-head-actions` margin-left:auto right-hug their comments already
   promise. On mobile this is a no-op (the content-filled width already
   equals the page box). `align-self: stretch` — NOT `width: 100%` — because
   stretch subtracts the item's own border+padding from the cross size while
   a percentage width does not: .kb-note is content-box (no box-sizing
   override, unlike the border-box .kb-panel), so width:100% measured 368px
   in the 334px page box and h-overflowed the document by 6px. */
.kb-task-page > .kb-board-head,
.kb-task-page > .kb-panel,
.kb-task-page > .kb-note { align-self: stretch; }

/* checklist card: same card metrics as .kb-panel; its head is the board-head
   pattern shrunk to content — section title + note meta on the left, actions
   (Open in Notes / Unlink, or the note-link form) hugging the right. */
.kb-note { margin: 16px 0; border: 1px solid #2a2d31; border-radius: 12px; background: #1b1d20; padding: 14px 16px; }
.kb-note-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.kb-note-head h2 { margin: 0; font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: #8a8f94; }
.kb-note-head .tools-muted { font-size: 12px; }
.kb-open-note, .kb-open-note:link, .kb-open-note:visited { color: #6fd3ff; text-decoration: none; font-size: 13px; }
.kb-open-note:hover { text-decoration: underline; }
.kb-note-items { list-style: none; margin: 0; padding: 0; }
.kb-note-item { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 14px; color: #e6e8ea; }
.kb-note-item + .kb-note-item { border-top: 1px solid #24272b; }
.kb-note-item-done .kb-note-item-text { color: #7a7f86; text-decoration: line-through; }
.kb-note-item-text { flex: 1 1 auto; }

/* transfer select */
.kb-transfer { flex: 0 1 180px; }

/* ── casino (spin pool) ──
   The "♠ Spin" button (board head) and the per-column ♠ toggle (Manage
   panel, admin+ only). Palette: gold for the pool (a "casino" read against
   the slate board surface), the existing red for errors. */
.kb-spin:disabled { opacity: .55; cursor: default; }
/* The task-page Save is JS-gated to the dirty state (kanban.js taskPage);
   the server HTML ships it ENABLED so the no-JS native POST still works —
   same greyed-out look as the spin button's disabled state. */
#kb-save:disabled { opacity: .55; cursor: default; }

/* A pool column's name glows gold so the spin set reads at a glance from
   the board itself; the spade after it names the mechanism (title= explains
   it — the checkbox in the Manage panel is where it is flipped). */
.kb-col-casino .kb-col-name { color: #e8c66a; }
.kb-col-casino-mark { margin-left: 6px; font-size: 12px; color: #e8c66a; }

/* The Manage-panel checkbox: quiet inline affordance next to the column
   name; its input is sized by the site defaults (the 16px mobile floor
   below still applies — it sits inside .kb-board). */
.kb-state-casino { margin-left: 8px; font-size: 13px; color: #e8c66a; white-space: nowrap; user-select: none; cursor: pointer; font-weight: 400; text-transform: none; letter-spacing: 0; }
.kb-state-casino input { margin: 0 4px 0 2px; vertical-align: middle; }

/* ── the slot machine ──
   The overlay is appended to <body> lazily (kanban.js ensureSpin) on the
   first Spin click; Close / backdrop click remove it, so nothing here is
   visible while idle. The reels animate via a ±2px jitter (CSS) while the
   JS steps the symbols — stopping the CSS class is what reads as "locked". */
.kb-spin-overlay { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; background: rgba(8, 10, 12, .78); padding: 16px; }
.kb-spin { background: #17191d; border: 1px solid #38505f; border-radius: 14px; box-shadow: 0 18px 60px rgba(0, 0, 0, .55); padding: 24px 26px; width: min(420px, 100%); box-sizing: border-box; text-align: center; }
.kb-spin-title { margin: 0 0 6px; font-size: 18px; font-weight: 700; color: #eef0f2; }
.kb-spin-hint { margin: 0 0 18px; font-size: 12.5px; color: #8a8f94; line-height: 1.45; }
.kb-spin-reels { display: flex; gap: 12px; justify-content: center; }
.kb-spin-reel { width: 88px; height: 88px; border-radius: 12px; background: #101215; border: 2px solid #2a2d31; display: flex; align-items: center; justify-content: center; font-size: 46px; line-height: 1; color: #e6e8ea; box-sizing: border-box; }
.kb-spin-reel.spinning { animation: kb-spin-jitter 90ms infinite alternate; border-color: #41505c; }
.kb-spin-reel.stopped { animation: none; }
.kb-spin-reel.lit { border-color: #e8c66a; box-shadow: 0 0 0 1px rgba(232, 198, 106, .35), 0 0 22px rgba(232, 198, 106, .28); color: #e8c66a; }
@keyframes kb-spin-jitter { from { transform: translateY(2px); } to { transform: translateY(-2px); } }
@media (prefers-reduced-motion: reduce) {
  .kb-spin-reel.spinning { animation: none; }
}
/* The result line: neutral while spinning, gold on a win (title + where it
   lives), the standard board red on a loss/error (same hue as .kb-flash.err
   and .kb-btn.kb-danger). */
.kb-spin-result { margin: 16px 0 0; font-size: 14px; color: #c8ccd1; min-height: 1.4em; line-height: 1.4; }
.kb-spin-result.win { color: #e8c66a; }
.kb-spin-result.err { color: #f0a0a0; }
.kb-spin-actions { display: flex; gap: 10px; justify-content: center; margin-top: 18px; }
/* The winner link sits under the actions row (outside it, so the flex row
   keeps two items); hidden until a spin lands a winner. The [hidden] rule is
   stated explicitly so an author display:none can't be outranked later. */
.kb-spin-open { margin-top: 12px; display: inline-block; }
.kb-spin-open[hidden] { display: none; }

/* The pickers (kanban.js ensureSpin): a left-aligned, scrollable strip of
   checkbox rows under the title — projects first (all checked by default),
   tags second (none checked). Both hide themselves when the board has
   nothing to offer, so a fresh org still sees the bare machine. Gold accent
   continues the casino read; the list scrolls so a many-project org doesn't
   turn the modal into a wall. */
.kb-spin-pick { margin: 0 0 14px; text-align: left; }
.kb-spin-pick h3 { margin: 0 0 6px; font-size: 13px; font-weight: 600; color: #a7b9c5; text-transform: uppercase; letter-spacing: .04em; }
.kb-spin-pick-list { display: flex; flex-wrap: wrap; gap: 6px 14px; max-height: 148px; overflow-y: auto; padding: 8px 10px; background: #101215; border: 1px solid #24272b; border-radius: 10px; }
.kb-spin-pick-opt { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: #c8ccd1; cursor: pointer; white-space: nowrap; }
.kb-spin-pick-opt input { margin: 0; width: 16px; height: 16px; accent-color: #e8c66a; flex: 0 0 auto; }
.kb-spin-pick-note { margin: 6px 0 0; font-size: 11.5px; color: #7a7f86; line-height: 1.4; }
@media (max-width: 700px) {
  /* The 16px input floor below targets .kb-board/.kb-page inputs; the
     overlay lives on <body>, so the pickers get their own (same reason:
     an under-16px input makes iOS Safari zoom the page on tap). */
  .kb-spin-pick-opt { font-size: 16px; }
  .kb-spin-pick-opt input { width: 18px; height: 18px; }
}

/* narrow: the manage grid stacks single-column, labels condense (the board
   frame + column overflow are handled by the `.kb-board`/`.kb-grid` rules
   above, at every width); the description gets a TALLER floor — typing
   long text on a small screen wants more lines visible, not a 4-line box. */
@media (max-width: 700px) {
  .kb-manage-grid { grid-template-columns: 1fr; }
  .kb-manage-col + .kb-manage-col { border-left: none; border-top: 1px solid #24272b; }
  .kb-row-label { padding: 6px 8px; font-size: 11px; }
  .kb-task-form textarea { min-height: 170px; }
  /* Form controls at the 16px mobile floor: mobile browsers (iOS Safari
     first among them) auto-zoom the whole page into focus of any input
     below 16px, so this is readability + no-jump behaviour, not just text. */
  .kb-page input, .kb-page select, .kb-page textarea,
  .kb-task-page input, .kb-task-page select, .kb-task-page textarea,
  .kb-board input, .kb-board select, .kb-board textarea { font-size: 16px; }
  /* Tap targets, measured on the 390 board: chip-x 19×24, + 34×33, kb-btn
     62–78×33 — below a comfortable thumb floor. Pad the small ones up and
     drop the 300ms tap delay / double-tap zoom on the controls a hand
     actually hits. Desktop keeps the compact metrics (a precise pointer
     doesn't need them). */
  .kb-chip-x { padding: 8px; min-width: 28px; min-height: 28px; }
  .kb-add-task { min-height: 40px; }
  .kb-btn, .kb-chip-x, .kb-add-task { touch-action: manipulation; }
}

/* ── desktop (≥701px) ──
    The kanban metrics above were sized down toward a 390px phone (that was
    the floor they were measured against); on a desktop they read small.
    This block is the desktop-UPSCALE pass: type and spacing bumped one step
    across the board (head, labels, cells, cards, chips, badges, buttons)
    and the task detail page (panels, note card, note items, form controls).
    Deliberately NOT touched here:
      * the 120px .kb-row-label track — a *width*, not a scale (server-inline
        grid columns + kanban.js's bindTrack); labels ellipsize (nowrap +
        ellipsis) and don't overflow;
      * the slot-machine overlay (.kb-spin*) — a self-contained 420px modal
        outside the board+task scope (its Spin / Close .kb-btns picking up
        the button bump is intentional).
    The `max-width: 700px` block above keeps its 16px input floor + tap
    targets (mutually exclusive viewports) and every base overflow-wrap /
    min-width: 0 / max-width: 100% h-overflow guard applies unchanged. */
@media (min-width: 701px) {
  /* headers / titles */
  .kb-head h1 { font-size: 30px; }
  .kb-board-head h1 { font-size: 26px; }
  .kb-task-top h1 { font-size: 28px; }
  .kb-row-title, .kb-row-title:link, .kb-row-title:visited { font-size: 16px; }
  .kb-row { padding: 16px 18px; }
  .kb-create summary { font-size: 16px; padding: 14px 18px; }

  /* board grid & cards */
  .kb-row-label { padding: 10px 14px; font-size: 14px; }
  .kb-col-head { padding: 12px 14px; font-size: 14px; }
  .kb-cell { padding: 10px; min-height: 84px; gap: 8px; }
  .kb-card { padding: 10px 13px; }
  .kb-card-title { font-size: 15px; }
  .kb-card-desc { font-size: 14px; margin-top: 4px; }
  .kb-card-tags { margin-top: 6px; }
  .kb-assignee { font-size: 13px; margin-top: 5px; }
  .kb-due { font-size: 13px; margin-top: 3px; }
  .kb-note-badge { font-size: 14px; }
  .kb-add-task { font-size: 14px; min-height: 36px; padding: 10px 14px; }
  .kb-addform { gap: 8px; }

  /* chips */
  .kb-chip { gap: 5px; font-size: 13px; padding: 5px 12px; }
  .kb-chip-lg { font-size: 15px; }
  .kb-chip-x { font-size: 14px; padding: 7px; }
  .kb-chip-more { font-size: 13px; }

  /* badges / buttons / meta text */
  .kb-badge { font-size: 13px; padding: 3px 11px; }
  .kb-btn { font-size: 15px; padding: 9px 16px; }
  .kb-btn.kb-mini { font-size: 13px; padding: 5px 11px; min-height: 34px; }
  .kb-flash { font-size: 14px; }
  .kb-back, .kb-back:link, .kb-back:visited { font-size: 15px; }
  .kb-sec-h { font-size: 14px; }

  /* manage panel */
  .kb-manage summary { font-size: 15px; padding: 14px 18px; }
  .kb-manage-grid { padding: 14px 0; }
  .kb-manage-col h3 { font-size: 13px; }
  .kb-member, .kb-state, .kb-project { font-size: 15px; padding: 8px 0; }
  .kb-member-list, .kb-state-list, .kb-project-list { margin: 0 0 12px; }
  .kb-state-casino { font-size: 14px; }

  /* form controls (index / board / task pages) */
  .kb-page input, .kb-page select, .kb-page textarea,
  .kb-task-page input, .kb-task-page select, .kb-task-page textarea,
  .kb-board input, .kb-board select, .kb-board textarea {
    font-size: 15px; padding: 10px 12px;
  }
  .kb-form { gap: 12px; }
  .kb-inline { margin-bottom: 12px; }

  /* task detail page */
  .kb-panel { padding: 18px 20px; margin: 18px 0; }
  .kb-panel-h { font-size: 13px; margin-bottom: 14px; }
  .kb-note { padding: 18px 20px; margin: 18px 0; }
  .kb-note-head h2 { font-size: 13px; }
  .kb-note-head .tools-muted { font-size: 13px; }
  .kb-open-note, .kb-open-note:link, .kb-open-note:visited { font-size: 14px; }
  .kb-note-item { font-size: 16px; padding: 9px 0; }
  .kb-task-form textarea { min-height: 140px; }
  /* Assignee + due: a side-by-side row instead of a stacked pair — with the
     panel finally full-width there is room to share it, and the two are
     visually "one tier" (short pickers vs Title/Description prose fields).
     The base label is a flex COLUMN with `align-items: flex-start` (so the
     control sits at its own width for tight mobile wrap); stretch the
     CROSS axis to the label instead so each control fills its half. flex-basis
     0 gives both labels equal halves regardless of content (a 40-char member
     name must not outsize the date field). min-width:180px from the base
     `.kb-in` rule is a no-op here at this width. */
  .kb-task-fields { flex-direction: row; gap: 16px; }
  .kb-task-fields label { flex: 1 1 0; align-items: stretch; }
}
