/* TomBot dashboard — TomBot design standard (feat-152, imp-20260613-30).
   Direction "Ink & Ember": cool ink structure on warm-stone neutrals, Pine
   accent (white text), gold warning. Canonical tokens + rationale live in
   docs/DESIGN-STANDARD.md (the single source of truth). Light + dark both
   defined; dark auto-follows the OS (prefers-color-scheme) and can be
   manually overridden via [data-theme] (set by the in-app toggle / Telegram). */

:root {
  /* Pine — light */
  --bg: #f4f3ef;
  --bg-alt: #faf9f5;
  --bg-card: #ffffff;
  --border: #e4e2db;
  --border-strong: #d8d5cc;
  --text: #21262e;
  --text-muted: #5e6672;
  --text-soft: #8a909a;
  --accent: #2e6e60;
  --accent-ink: #ffffff;
  --accent-soft: rgba(46, 110, 96, 0.10);
  --warn: #c99a3a;
  --warn-soft: rgba(201, 154, 58, 0.13);
  --alarm: #b23a2e;
  --alarm-soft: rgba(178, 58, 46, 0.12);
  --success: #3f6b53;
  --shadow: 0 1px 3px rgba(40, 30, 20, 0.06);
  /* type + shape tokens */
  --font-display: "Newsreader", Georgia, "Times New Roman", serif;
  --font-body: "Familjen Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --radius-sm: 7px;
  --radius-md: 10px;
}

/* Pine — dark (shared by OS-auto + manual override). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #16191d;
    --bg-alt: #23282f;
    --bg-card: #1e2228;
    --border: #313842;
    --border-strong: #3e4651;
    --text: #e8e6e0;
    --text-muted: #9aa0a8;
    --text-soft: #7a828c;
    --accent: #4e9c84;
    --accent-ink: #0c1512;
    --accent-soft: rgba(78, 156, 132, 0.18);
    --warn: #d9b25a;
    --warn-soft: rgba(217, 178, 90, 0.16);
    --alarm: #d5705f;
    --alarm-soft: rgba(213, 112, 95, 0.18);
    --success: #5e9b79;
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }
}
:root[data-theme="dark"] {
  --bg: #16191d;
  --bg-alt: #23282f;
  --bg-card: #1e2228;
  --border: #313842;
  --border-strong: #3e4651;
  --text: #e8e6e0;
  --text-muted: #9aa0a8;
  --text-soft: #7a828c;
  --accent: #4e9c84;
  --accent-ink: #0c1512;
  --accent-soft: rgba(78, 156, 132, 0.18);
  --warn: #d9b25a;
  --warn-soft: rgba(217, 178, 90, 0.16);
  --alarm: #d5705f;
  --alarm-soft: rgba(213, 112, 95, 0.18);
  --success: #5e9b79;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.45;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* Environment marker — staging only (imp follow-up 2026-06-15). A loud,
   unmistakable gold bar + topbar tint so prod and staging can't be confused.
   The element is hidden on prod (detectContext only reveals it on the staging
   host); the data-env tint rules are scoped to [data-env="staging"]. */
.env-banner {
  position: sticky;
  top: 0;
  z-index: 11;
  background: var(--warn);
  color: #1c1505;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  padding: 5px 12px;
}
:root[data-env="staging"] .topbar {
  background: var(--warn-soft);
  border-bottom: 2px solid var(--warn);
  top: 26px; /* sit below the sticky banner */
}

/* topbar */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px 8px;
  position: sticky;
  top: 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  z-index: 10;
}

.topbar h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.topbar .meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  color: var(--text-soft);
}

.refresh-btn {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 14px;
  cursor: pointer;
}

.refresh-btn:active { transform: scale(0.95); }

/* tabs */
.tabs {
  display: flex;
  gap: 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 49px;
  z-index: 9;
}

.tab {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--text-muted);
  padding: 10px 6px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}

.tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.tab-panel { display: none; padding: 12px 12px 80px; }
.tab-panel.active { display: block; }

/* filter chips */
.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}

.chip {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 12px;
  cursor: pointer;
}

.chip.active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* cards */
.card-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Author-level `display:flex` on .card-list / .goals-focus / .goals-lanes
 * outranks the UA `[hidden]{display:none}` rule, so the HTML `hidden`
 * attribute alone doesn't hide them. Override with attribute selectors
 * that have the specificity to win (the goals view toggle relies on this).
 * The .goals-focus / .goals-lanes pair was missed in the feat-054→#254
 * redesign, which left #goals-list visible in Map view (the focus list
 * stayed on screen and pushed the map below the fold on phones). */
.card-list[hidden],
.goals-map[hidden],
.goals-focus[hidden],
.goals-lanes[hidden] {
  display: none;
}

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  box-shadow: var(--shadow);
}

.card.urgent { border-color: var(--warn); }
.card.done { opacity: 0.6; }

/* feat-224 — build-queue drag-to-reorder affordance. Draggable cards
   show a move cursor + a subtle grab handle accent on the left edge; the
   card being dragged dims so the drop target reads clearly. */
.card.draggable {
  cursor: grab;
  border-left: 3px solid var(--border);
}
/* imp-20260623-07 — combined-state guard: an URGENT card's accent border
   must enclose all four sides. The `.card.draggable` rule above re-declares
   `border-left` (the drag-handle affordance), which — at equal specificity but
   later in source — would otherwise strip the accent colour from the left edge
   of a card that is both `.urgent` and `.draggable`, leaving a three-sided
   highlight. Re-assert the accent colour on the left edge here (higher
   specificity) while keeping the 3px handle thickness so the drag affordance
   still reads. Reuses the existing `--warn` token; introduces no new colour.
   Keep this rule if any future left-edge override is added. */
.card.urgent.draggable {
  border-left: 3px solid var(--warn);
}
.card.draggable:active { cursor: grabbing; }
.card.dragging {
  opacity: 0.5;
  cursor: grabbing;
}

/* imp-20260706-23 (feat-543) — a pinned build-queue card carries a distinct
   left-edge accent so the top-precedence pin is obvious at a glance, beyond
   sorting first and being non-draggable (FR6). A pinned card is never
   draggable, so this can't collide with the `.card.draggable` left border.
   Reuses the existing `--accent` token (distinct from urgent's `--warn`); no
   new colour is introduced. */
.card.pinned {
  border-left: 3px solid var(--accent);
}

.card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.card-id {
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: 0.02em;
}

/* Secondary id (imp-YYYYMMDD-NN when a feat-NNN is the primary).
   Smaller, lighter, sits inline with the primary so both are visible
   at a glance without competing visually. */
.card-id-secondary {
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 10px;
  color: var(--text-soft);
  letter-spacing: 0.02em;
  margin-left: 8px;
  opacity: 0.75;
}

.card-meta {
  display: flex;
  gap: 6px;
  align-items: center;
  font-size: 11px;
  color: var(--text-soft);
}

.badge {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.badge.high { color: var(--warn); border-color: var(--warn); }
.badge.urgent { background: var(--warn); color: #fff; border-color: var(--warn); }
.badge.alarm { background: var(--alarm); color: #fff; border-color: var(--alarm); font-weight: 700; }
.badge.done { color: var(--success); border-color: var(--success); }
/* feat-102 — in-flight pipeline states. pr-open reads as "active in PR"
   (accent outline); pending-deploy as "merged, almost live" (accent
   filled-soft) — a visible progression toward done (green). */
.badge.pr-open { color: var(--accent); border-color: var(--accent); }
.badge.pending-deploy { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
/* feat-271 — core-shipped: core is live (success outline) with tail work
   pending. Distinct from the neutral "blocked" badge so a shipped-with-tail
   feature never reads as stuck. */
.badge.core-shipped { color: var(--success); border-color: var(--success); background: transparent; }
/* feat-488 — shipped-pending-verify: code live but the OUTCOME is unverified.
   A warning tone so "shipped, pending verification" is visibly distinct from a
   verified `done` (FR9) — it still needs proving. */
.badge.shipped-pending-verify { color: var(--warning, #b8860b); border-color: var(--warning, #b8860b); background: transparent; }
/* feat-271 — the follow-up roll-up chip ("core live · N pending / M done").
   Pairs with the badge to convey honest progress at a glance (FR-10). */
.followup-chip {
  display: inline-flex;
  align-items: center;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  background: var(--success-soft, rgba(46, 160, 67, 0.15));
  color: var(--success);
  white-space: nowrap;
}

.card-title {
  font-size: 14px;
  font-weight: 600;
  margin: 4px 0 4px 0;
  color: var(--text);
}

.card-summary {
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.4;
}

.card-summary p { margin: 0 0 4px 0; }

/* expanded view */
.card-body {
  display: none;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
}

.card.expanded .card-body { display: block; }

.card-body h1, .card-body h2, .card-body h3, .card-body h4 {
  margin: 8px 0 4px 0;
  line-height: 1.25;
}

.card-body h1 { font-size: 16px; }
.card-body h2 { font-size: 15px; }
.card-body h3 { font-size: 14px; }
.card-body h4 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }

.card-body code {
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  background: var(--bg-alt);
  padding: 1px 4px;
  border-radius: 3px;
}

.card-body pre {
  background: var(--bg-alt);
  padding: 8px 10px;
  border-radius: 6px;
  overflow-x: auto;
  margin: 6px 0;
  font-size: 12px;
}

.card-body pre code {
  background: transparent;
  padding: 0;
}

.card-body ul, .card-body ol {
  padding-left: 18px;
  margin: 4px 0;
}

.card-body table {
  border-collapse: collapse;
  width: 100%;
  font-size: 12px;
  margin: 6px 0;
}

.card-body th, .card-body td {
  border: 1px solid var(--border);
  padding: 4px 6px;
  text-align: left;
}

.card-body th { background: var(--bg-alt); }

.card-body blockquote {
  border-left: 3px solid var(--border-strong);
  margin: 6px 0;
  padding: 2px 0 2px 10px;
  color: var(--text-muted);
}

.card-body a { color: var(--accent); text-decoration: none; }
.card-body strong { font-weight: 700; color: var(--text); }
.card-body em { font-style: italic; }

/* asks list — flatter than imp cards */
.ask-row {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ask-row.done { opacity: 0.5; }
.ask-row.done .ask-title { text-decoration: line-through; }

/* feat-546 — owner-split asks view. Right-align the open/done/all filter
   (mirrors the features-view affordance), label each owner section, and give
   the drag-to-reorder (TomBot section only) the same affordance as the
   build-queue cards. */
.asks-filter-row { justify-content: flex-end; }

/* Asks toolbar: owner toggle on the left, open/done/all filter on the right,
   wrapping gracefully on narrow screens. */
.asks-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}
.asks-toolbar .asks-filter-row { margin: 0; }

/* Owner segmented toggle — same pill treatment as the goals-view toggle. */
.asks-owner-toggle {
  display: inline-flex;
  padding: 3px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  gap: 2px;
}
.asks-owner-chip {
  appearance: none;
  border: none;
  background: transparent;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
  padding: 6px 16px;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
  letter-spacing: 0.01em;
}
.asks-owner-chip:hover { color: var(--text); }
.asks-owner-chip.active {
  background: var(--bg-card);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 0 0 0.5px var(--border);
}
@media (prefers-color-scheme: dark) {
  .asks-owner-chip.active {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 0 0 0.5px var(--border-strong);
  }
}

.asks-section { margin-bottom: 18px; }

.ask-row.draggable {
  cursor: grab;
  border-left: 3px solid var(--border);
}
.ask-row.draggable:active { cursor: grabbing; }
.ask-row.dragging {
  opacity: 0.5;
  cursor: grabbing;
}

.ask-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.ask-title {
  font-weight: 600;
  font-size: 14px;
  flex: 1;
}

.ask-age {
  font-size: 11px;
  color: var(--text-soft);
  white-space: nowrap;
}

.ask-age.old { color: var(--warn); font-weight: 600; }

.ask-detail {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.4;
}

/* placeholder for tabs not yet built */
.placeholder {
  text-align: center;
  padding: 40px 20px;
  color: var(--text-soft);
  font-size: 14px;
}

.placeholder.placeholder-soft {
  padding: 16px 12px;
  font-size: 12px;
}

/* imp-20260624-04 (feat-348) — actionable "deploy button not ready" state when
   the production deploy credential is missing/expired (surfaced by the probe). */
.placeholder.placeholder-warn {
  color: var(--warn);
  background: var(--warn-soft);
  border-radius: 6px;
}

.placeholder code {
  font-family: "SF Mono", Menlo, Consolas, monospace;
  background: var(--bg-alt);
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 12px;
}

/* search + sort row (P2) */
.search-row {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
  align-items: center;
}

.search-input {
  flex: 1;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--text);
  font-family: inherit;
  -webkit-appearance: none;
  appearance: none;
}

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

.sort-select {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font-size: 12px;
  color: var(--text-muted);
  font-family: inherit;
  cursor: pointer;
}

.sort-select:focus {
  outline: none;
  border-color: var(--accent);
}

/* goals tab (P2) */
.goal-card {
  cursor: pointer;
}

.goal-body {
  display: none;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.goal-card.expanded .goal-body { display: block; }

.goal-section { margin-top: 10px; }
.goal-section:first-child { margin-top: 0; }

.goal-section h4 {
  margin: 0 0 6px 0;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.goal-section p,
.goal-section ul,
.goal-section ol {
  font-size: 13px;
  color: var(--text);
  line-height: 1.45;
}

.goal-empty-callout {
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  color: var(--warn);
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 12px;
  font-style: italic;
}

.initiative {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  margin-bottom: 6px;
  background: var(--bg-alt);
}

.initiative-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
}

.initiative-name {
  font-weight: 600;
  font-size: 13px;
}

.initiative-status {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-soft);
  white-space: nowrap;
}

.initiative-body {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.45;
}

.initiative-body ul,
.initiative-body ol { padding-left: 16px; margin: 4px 0; }

.initiative-body p { margin: 4px 0; }

/* ---- Goals: Focus view (Direction A) — proactive priority list ---- */
.goals-focus { display: flex; flex-direction: column; gap: 10px; }
.gf-card {
  display: grid; grid-template-columns: 12px 1fr 158px; gap: 16px; align-items: start;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 15px 18px; cursor: pointer;
}
.gf-card.attn { border-color: var(--warn); }
.gf-card.paused { opacity: 0.62; }
.gf-dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 6px; background: var(--text-soft); }
.gf-dot.active { background: var(--accent); }
.gf-main { min-width: 0; }
.gf-title { font-family: var(--font-display); font-size: 17px; font-weight: 600; color: var(--text); line-height: 1.2; }
.gf-meta { font-size: 12.5px; color: var(--text-muted); margin-top: 4px; }
.gf-meta b { color: var(--text); font-weight: 600; }
.gf-bar { height: 5px; border-radius: 3px; background: var(--bg-alt); margin-top: 9px; max-width: 320px; overflow: hidden; }
.gf-bar > i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.gf-right { border-left: 1px solid var(--border); padding-left: 14px; }
.gf-lbl { font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-soft); }
.gf-act { font-size: 12.5px; color: var(--text); margin-top: 3px; line-height: 1.3; }
.gf-act.nudge { color: var(--accent); font-weight: 600; }
.gf-detail { display: none; grid-column: 1 / -1; margin-top: 6px; padding-top: 10px; border-top: 1px solid var(--border); }
.gf-card.expanded .gf-detail { display: block; }

/* ---- Goals: Lanes view (Direction B) — each goal + its initiatives ---- */
.goals-lanes { display: flex; flex-direction: column; }
.gl-lane { display: grid; grid-template-columns: 248px 1fr; gap: 18px; align-items: center; padding: 16px 4px; border-top: 1px solid var(--border); }
.gl-lane:last-child { border-bottom: 1px solid var(--border); }
.gl-lane.paused { opacity: 0.62; }
.gl-goal { display: flex; gap: 13px; align-items: center; }
.gl-ring { width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; flex: none; border: 2.5px solid var(--accent); }
.gl-ring.paused { border-color: var(--text-soft); }
.gl-ring > span { font-size: 15px; font-weight: 600; color: var(--accent); font-family: var(--font-display); }
.gl-ring.paused > span { color: var(--text-soft); }
.gl-t { font-family: var(--font-display); font-size: 16px; font-weight: 600; color: var(--text); line-height: 1.15; }
.gl-s { font-size: 10.5px; letter-spacing: 0.05em; text-transform: uppercase; margin-top: 3px; color: var(--text-soft); }
.gl-s.active { color: var(--accent); }
.gl-inits { display: flex; gap: 9px; flex-wrap: wrap; }
.gl-init { background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px; min-width: 138px; }
/* Lanes cards open the same detail sheet as the map view — show it. */
.gl-clickable { cursor: pointer; }
.gl-init.gl-clickable:hover { border-color: var(--accent); }
.gl-goal.gl-clickable:hover .gl-t { color: var(--accent); }
/* msg 6119: status as a labelled dot (on the status line) — not an
   unlabelled coloured left edge. The "open question" card keeps its warn
   treatment because it's a labelled action prompt, not a status. */
.gl-init.next { border-color: var(--warn); }
.gl-init.next .gl-it { color: var(--warn); }
.gl-it { font-size: 12.5px; font-weight: 600; color: var(--text); }
.gl-is { font-size: 11px; color: var(--text-muted); margin-top: 4px; display: inline-flex; align-items: center; gap: 5px; }
.gl-init.active .gl-is::before,
.gl-init.paused .gl-is::before { content: ""; width: 6px; height: 6px; border-radius: 50%; flex: none; }
.gl-init.active .gl-is::before { background: var(--accent); }
.gl-init.paused .gl-is::before { background: var(--text-muted); }
.gl-empty { color: var(--text-soft); font-size: 12.5px; font-style: italic; }

/* schedule tab (P2) */
.schedule-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.schedule-section { display: block; }

.schedule-heading {
  margin: 0 0 6px 2px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.schedule-card { margin-bottom: 6px; }

.schedule-card .card-title {
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
}

.schedule-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 8px;
  font-size: 11px;
  color: var(--text-soft);
}

.schedule-meta-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-weight: 500;
  letter-spacing: 0.01em;
  cursor: default;
}

.schedule-meta-chip-alert {
  border-color: var(--alarm);
  color: var(--alarm);
  background: var(--alarm-soft);
  font-weight: 600;
}

.schedule-meta-chip-info {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}

.schedule-card .cron-string {
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  color: var(--text-muted);
  background: var(--bg-alt);
  padding: 2px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
}

.next-runs {
  margin-top: 8px;
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.6;
}

.next-runs-label {
  color: var(--text-soft);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-right: 4px;
}

.schedule-actions {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
}

.schedule-description {
  margin-top: 10px;
  font-size: 13px;
  color: var(--text-muted);
}

.schedule-description summary {
  cursor: pointer;
  font-weight: 500;
  font-size: 12px;
  color: var(--accent);
  padding: 4px 0;
  list-style: none;
}
.schedule-description summary::marker { display: none; }
.schedule-description summary::-webkit-details-marker { display: none; }
.schedule-description summary::before {
  content: "▸ ";
  display: inline-block;
  transition: transform 0.18s ease;
  margin-right: 2px;
}
.schedule-description[open] summary::before {
  transform: rotate(90deg);
}
.schedule-description-body {
  margin-top: 6px;
  padding: 8px 10px;
  background: var(--bg-alt);
  border-radius: 8px;
  line-height: 1.55;
  color: var(--text);
  font-size: 13px;
}

/* "Firing now" indicator (imp-20260514-02). Toggled by
   tickScheduleFiringStatus() when wall-clock is within ±2 min of the
   routine's soonest next_run. Kept compact so it sits naturally in the
   card-meta row next to the kind badge. */
.firing-chip[hidden] { display: none; }
.firing-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--alarm);
  background: var(--alarm-soft);
  color: var(--alarm);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  animation: firing-pulse 1.6s ease-in-out infinite;
}
.schedule-card.is-firing-now {
  border-color: var(--alarm);
  box-shadow: 0 0 0 1px var(--alarm-soft);
}
@keyframes firing-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.75; transform: scale(0.98); }
}
@media (prefers-reduced-motion: reduce) {
  .firing-chip { animation: none; }
}

.schedule-header {
  margin-bottom: 14px;
  padding: 10px 12px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.schedule-header-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
}

.schedule-header-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 12px;
}

.schedule-header-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-muted);
  font-weight: 500;
}

.schedule-header-chip-active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  font-weight: 600;
}

.schedule-header-actions {
  display: flex;
  gap: 6px;
}

.tz-hint {
  color: var(--text-soft);
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 10px;
}

.bg-card.running { border-color: var(--accent); }
.bg-card.queued { opacity: 0.85; }
.bg-card.failed { border-color: var(--warn); }

/* feat-204 (imp-20260619-04) — Running-now task_run cards. A live progress
   panel in the Maintenance (Schedule) area: each active background process
   with a progress bar, status line, and relative last-heartbeat time. A
   stalled run (heartbeat aged out server-side) and terminal runs are styled
   distinctly so a stall or completion reads at a glance. */
.task-run-card.running { border-color: var(--accent); }
.task-run-card.waiting { border-color: var(--warn); }
.task-run-card.succeeded { border-color: var(--success); }
.task-run-card.failed { border-color: var(--alarm); }
/* Stalled — a running run that stopped heartbeating: the strongest signal. */
.task-run-card.stale { border-color: var(--alarm); background: var(--alarm-soft); }
/* Terminal runs render only in the History view, dimmed. */
.task-run-card.terminal { opacity: 0.7; }
/* feat-383 — History (Recently completed) card: a compact terminal card with a
   PR link. The outcome is carried by the .succeeded/.failed border above. */
.task-run-card.history .next-runs {
  display: flex;
  gap: 10px;
  align-items: center;
}
.task-run-pr-link {
  font-size: 11px;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 0 6px;
  line-height: 18px;
  text-decoration: none;
}
.task-run-pr-link:hover { background: var(--accent); color: var(--bg, #fff); }

/* feat-422 (imp-20260630-07) — collapsed completed-runs view. The older
   completed runs beyond the display default fold behind this expander; the
   button reveals them and states how many are hidden. */
.task-run-history-more.hidden { display: none; }
.task-run-history-expander {
  display: block;
  width: 100%;
  margin: 6px 0 2px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--muted, #888);
  background: transparent;
  border: 1px dashed var(--border, #ddd);
  border-radius: 8px;
  cursor: pointer;
  text-align: center;
}
.task-run-history-expander:hover { color: var(--accent); border-color: var(--accent); }

.env-chip {
  font-size: 11px;
  color: var(--muted, #888);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0 6px;
  line-height: 18px;
}

.task-run-progress {
  margin-top: 8px;
  height: 6px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.task-run-bar {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.4s ease;
}
.task-run-card.stale .task-run-bar { background: var(--alarm); }
.task-run-card.failed .task-run-bar { background: var(--alarm); }
.task-run-card.succeeded .task-run-bar { background: var(--success); width: 100%; }

/* Indeterminate (unknown total): an animated sweep rather than a misleading
   0%/100% fill. Honors reduced-motion (falls back to a static partial fill). */
.task-run-bar.indeterminate {
  width: 40%;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: task-run-sweep 1.4s ease-in-out infinite;
}
@keyframes task-run-sweep {
  0% { margin-left: -40%; }
  100% { margin-left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .task-run-bar.indeterminate { animation: none; margin-left: 30%; }
}

/* P3 — write-back action buttons + toast */

.card-actions {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.action-btn {
  flex: 1;
  min-width: 70px;
  background: var(--bg-alt);
  border: 1px solid var(--border-strong);
  color: var(--text);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.12s, border-color 0.12s, transform 0.05s;
}

.action-btn:active { transform: scale(0.97); }

.action-btn.approve {
  background: rgba(52, 199, 89, 0.12);
  border-color: var(--success);
  color: var(--success);
}

.action-btn.reject {
  background: rgba(255, 59, 48, 0.10);
  border-color: #ff3b30;
  color: #ff3b30;
}

.action-btn.discuss {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* feat-078 / msg 3171: Build Now occupies the primary CTA slot on
   build-queued cards (same visual weight as Approve on other stages).
   The "queued" state after a tap reads as a passive label, not an
   action — matches the disabled-button shape. */
.action-btn.build-now.primary {
  background: var(--success-soft, rgba(54, 178, 96, 0.18));
  border-color: var(--success, #36b260);
  color: var(--success, #36b260);
}

.action-btn.build-now.build-now-queued {
  background: var(--bg-muted, rgba(255, 255, 255, 0.05));
  border-color: var(--border, rgba(255, 255, 255, 0.12));
  color: var(--muted, rgba(255, 255, 255, 0.5));
}

/* imp-20260706-23 (feat-543) — pin/unpin toggle on build-queued cards.
   The "Pin" affordance reads as a neutral secondary action; once pinned the
   `.pinned` state fills in with the accent so the active pin is unmistakable
   (FR6), mirroring how Build Now's queued state reads as an active label. */
.action-btn.pin-toggle {
  background: var(--bg-alt);
  border-color: var(--border-strong);
  color: var(--text);
}
.action-btn.pin-toggle.pinned {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* feat-535 (imp-20260706-16) — two-way asks: the Answer affordance is the
   primary action on an answerable ask, so it carries the accent fill (the same
   token Discuss uses) to read as an active input CTA distinct from the neutral
   Mark done / Discuss controls. */
.action-btn.answer {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* feat-535 — the ask's declared answer prompt, shown on the card above the
   action row so the requested input is visible without opening the modal. */
.ask-answer-prompt {
  font-size: 12px;
  color: var(--accent);
  margin-top: 4px;
}

/* feat-535 bundle — owner reassignment toggle (My asks ↔ TomBot asks). A neutral
   secondary control, matching the pin-toggle's unstyled weight so it reads as an
   organisational move, not a primary input action. */
.action-btn.move-owner {
  background: var(--bg-alt);
  border-color: var(--border-strong);
  color: var(--text);
}

.action-btn[disabled],
.action-btn.in-flight {
  opacity: 0.5;
  cursor: progress;
}

/* feat-078: build-window label on build-queued cards. */
.build-window-label {
  font-size: 12px;
  color: var(--muted, rgba(255, 255, 255, 0.5));
  padding: 4px 0;
}
.build-window-label.muted {
  font-style: italic;
}
.build-window-label.expedited {
  color: var(--success, #36b260);
}

/* msg 3171: spec-pending state — shown when an imp is spec-ready but
   has no feat_slug yet (the drafter hasn't picked it up). Sits between
   the card body and the action row, similar weight to the build-window
   label so the user sees status at a glance. */
.spec-pending-label {
  font-size: 12px;
  color: var(--muted, rgba(255, 255, 255, 0.5));
  font-style: italic;
  padding: 4px 0;
}

/* Toast */
.toast {
  position: fixed;
  left: 50%;
  bottom: 56px;
  transform: translateX(-50%) translateY(20px);
  background: var(--text);
  color: var(--bg);
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 13px;
  max-width: 88%;
  text-align: center;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s;
  z-index: 100;
}

.toast.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.toast code {
  font-family: "SF Mono", Menlo, Consolas, monospace;
  background: rgba(255, 255, 255, 0.15);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 11px;
}

/* footer */
.footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--bg);
  border-top: 1px solid var(--border);
  padding: 8px 16px;
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--text-soft);
  padding-bottom: max(8px, env(safe-area-inset-bottom));
}

.footer-mark {
  font-family: "SF Mono", Menlo, Consolas, monospace;
}

.context-badge {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 6px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.context-badge.tg {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* error banner */
.error {
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  color: var(--warn);
  padding: 10px 12px;
  border-radius: 6px;
  margin-bottom: 12px;
  font-size: 13px;
}

/* feat (imp-20260615-02 FR-12): graceful error boundary. Hidden until a hard
   load failure flips body.app-error-active, which hides the tabs + panels and
   shows only this card. Plain-language + retry-first; the technical detail is
   logged for telemetry, never shown. Styled with the design-standard (Pine)
   tokens. */
.app-error {
  display: none;
  padding: 56px 20px;
  justify-content: center;
}
body.app-error-active .app-error { display: flex; }
body.app-error-active main > .tab-panel,
body.app-error-active main > aside,
body.app-error-active nav.tabs { display: none; }
.app-error-card {
  max-width: 360px;
  text-align: center;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 30px 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 13px;
}
.app-error-mark {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--warn-soft);
  color: var(--warn);
  font-family: var(--font-display);
  font-size: 23px;
  font-weight: 600;
  line-height: 1;
}
.app-error-title {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  color: var(--text);
  margin: 0;
}
.app-error-body {
  font-size: 14px;
  color: var(--text-muted);
  margin: 0;
  line-height: 1.5;
}
.app-error-retry {
  margin-top: 4px;
  padding: 9px 22px;
  background: var(--accent);
  color: var(--accent-ink);
  border: none;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.15s ease, transform 0.1s ease;
}
.app-error-retry:hover { opacity: 0.92; }
.app-error-retry:active { transform: translateY(1px); }
.app-error-hint {
  font-size: 12px;
  color: var(--text-soft);
  margin: 0;
  line-height: 1.45;
}

/* feat-033 — Feature card stage strip
   Four pills + connectors L→R: Concept → Spec → Build → Done.
   Variants: .done (solid fill), .current (outlined fill),
   .upcoming (ghost). Blocked statuses (rejected, blocked-needs-*) get
   .stage-strip.blocked which strikes through and adds a chip below.
   All theming via existing CSS variables — dark mode works for free. */
.stage-strip {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 6px 0 10px;
  flex-wrap: wrap;
  cursor: pointer;
}

.stage {
  border-radius: 999px;
  padding: 3px 9px;
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-soft);
  line-height: 1.4;
}

.stage.done {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}

.stage.current {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
  font-weight: 600;
}

.stage.upcoming {
  background: transparent;
  color: var(--text-soft);
  border: 1px dashed var(--border);
}

.stage-conn {
  flex: 1 1 auto;
  height: 2px;
  min-width: 6px;
  max-width: 24px;
  background: var(--border);
  border-radius: 1px;
}

.stage-conn.done {
  background: var(--accent);
}

.stage-strip.blocked .stage,
.stage-strip.blocked .stage-conn {
  text-decoration: line-through;
  opacity: 0.55;
  filter: grayscale(60%);
}

.stage-chip {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--warn-soft);
  border: 1px solid var(--warn);
  color: var(--warn);
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
}

.stage-chip.stage-chip-warn {
  /* Same chrome as the warn variant; semantic alias for "unknown status". */
}

@media (max-width: 280px) {
  .stage-strip {
    flex-direction: column;
    align-items: stretch;
  }
  .stage-conn {
    width: 2px;
    height: 8px;
    max-width: none;
    margin: 0 auto;
  }
}

/* feat-108 P4 — drain-disposition chip on build-queued Feature cards.
 * Sits in the card-meta row alongside priority + status badges. Four
 * variants matched to existing palette tokens so the chips read as
 * peers of the other meta badges (not louder, not quieter). */
.drain-chip {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  border: 1px solid;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.drain-chip-next {
  background: rgba(52, 199, 89, 0.12);
  border-color: var(--success);
  color: var(--success);
}

.drain-chip-queued {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

.drain-chip-backlog {
  background: var(--bg-alt);
  border-color: var(--border-strong);
  color: var(--text-muted);
}

.drain-chip-pinned {
  background: var(--warn-soft);
  border-color: var(--warn);
  color: var(--warn);
  font-weight: 600;
}

/* feat-034 — spec/plan PDF chips on Feature cards */
.card-spec-links {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 6px;
}

.spec-link {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 4px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text-soft);
  font-size: 11px;
  text-decoration: none;
  white-space: nowrap;
}

.spec-link:hover,
.spec-link:focus {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* ---------- PR link on in-flight cards (feat-131) ---------- */

.card-pr-link {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 6px;
}

/* Mirrors .spec-link affordance so the in-flight PR link reads as a sibling
   chip rather than a new motif (per the plan's frontend-design guidance). */
.pr-link {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 4px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text-soft);
  font-size: 11px;
  text-decoration: none;
  white-space: nowrap;
}

.pr-link:hover,
.pr-link:focus {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

.pr-sha {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 11px;
  color: var(--text-soft);
}

/* ---------- Reject metadata on a rejected card (feat-541) ---------- */

.reject-meta {
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 6px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
}

.reject-meta-reason {
  /* Untrusted free text: preserve author whitespace / newlines, but wrap and
     break long tokens so a pasted blob can't overflow the card. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 12px;
  color: var(--text-soft);
}

.reject-meta-none {
  font-style: italic;
  color: var(--text-muted);
}

.reject-meta-attribution {
  margin-top: 4px;
  font-size: 11px;
  color: var(--text-muted);
}

/* ---------- Reusable text-entry modal (feat-541 promptForText) ---------- */

.text-prompt-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.45);
}

.text-prompt {
  width: 100%;
  max-width: 420px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
}

.text-prompt-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}

.text-prompt-message {
  margin-top: 4px;
  font-size: 12px;
  color: var(--text-muted);
}

.text-prompt-input {
  width: 100%;
  margin-top: 10px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg-alt);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  resize: vertical;
  box-sizing: border-box;
}

.text-prompt-input:focus {
  outline: none;
  border-color: var(--accent);
}

.text-prompt-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 12px;
}

/* ---------- Goals view: List | Map toggle (feat-054) ---------- */

.goals-view-toggle {
  display: inline-flex;
  margin: 6px 0 14px;
  padding: 3px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  gap: 2px;
}

.goals-view-chip {
  appearance: none;
  border: none;
  background: transparent;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted);
  padding: 6px 16px;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
  letter-spacing: 0.01em;
}

.goals-view-chip:hover {
  color: var(--text);
}

.goals-view-chip.active {
  background: var(--bg-card);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 0 0 0.5px var(--border);
}

@media (prefers-color-scheme: dark) {
  .goals-view-chip.active {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 0 0 0.5px var(--border-strong);
  }
}

/* ---------- Goals mind-map canvas (feat-054) ---------- */

.goals-map {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 16px;
  /* msg 6082: flat, calm canvas — dropped the blue/green radial-gradient
     blobs that added visual noise and competed with the cards. */
  background: var(--bg-alt);
  overflow: hidden;
  min-height: 60vh;
}

.goals-map-canvas {
  /* msg 4817 redesign: was absolute-positioned for Cytoscape's canvas.
     Now flow-layout so the tree can scroll if it grows past viewport. */
  position: relative;
  width: 100%;
  min-height: 60vh;
  padding: 24px 16px 32px;
  overflow: auto;
}

/* ---------- Goals tree (msg 4817) — top-down hierarchical layout ---------- */

.goals-tree {
  display: flex;
  flex-wrap: nowrap;
  gap: 32px;
  align-items: flex-start;
  justify-content: center;
  min-width: min-content;
}

.goal-branch {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 200px;
}

.g-node {
  appearance: none;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  width: 100%;
  max-width: 240px;
  padding: 12px 14px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  /* msg 6119: keep the soft 3D shadow (Gareth likes it). The unclear bit
     was the left colour stripe — that's gone; status now reads as a
     labelled dot+word pill (see .g-node__chip--dot below). */
  box-shadow: var(--shadow);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform 80ms ease, border-color 120ms ease, box-shadow 120ms ease;
}

.g-node:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

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

.g-node:active {
  transform: translateY(0);
}

.g-node--goal {
  /* Goals are heavier than initiatives — they're the root of each branch. */
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: 0.005em;
  border-width: 1.5px;
  border-color: var(--border-strong);
}

.g-node--initiative {
  font-size: 14px;
  font-weight: 500;
}

.g-node__label {
  display: block;
  line-height: 1.35;
}

.g-node__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.g-node__chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--bg-alt);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.6;
  border: 1px solid var(--border);
}

/* msg 6119 (option 1): status pills carry a leading colour dot so the
   colour is always paired with the word. Dot colour set per status. */
.g-node__chip--dot { gap: 5px; padding-left: 6px; }
.g-node__chip--dot::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--chip-dot, var(--text-soft));
  flex: none;
}
.g-node__chip.dot-active { --chip-dot: var(--accent); }
.g-node__chip.dot-paused { --chip-dot: var(--text-muted); }
.g-node__chip.dot-done   { --chip-dot: var(--success); }
.g-node__chip.dot-other  { --chip-dot: var(--border-strong); }
/* exceptions also tint the word, to draw the eye a little more */
.g-node__chip.dot-stale  { --chip-dot: var(--warn); color: var(--warn); }
.g-node__chip.dot-alarm  { --chip-dot: var(--alarm); color: var(--alarm); }

/* ---------- Initiative tasks — third tier (msg 4833) ----------
 *
 * Attached to the initiative card as a stacked list, not rendered as
 * separate tree nodes. This keeps tree width bounded even when
 * initiatives have 5+ tasks each.
 */

.g-node__tasks {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--border);
  width: 100%;
}

.g-node__task {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.35;
  color: var(--text-muted);
}

.g-node__task-marker {
  flex: 0 0 auto;
  font-size: 11px;
  width: 12px;
  text-align: center;
  color: var(--text-soft);
  font-weight: 600;
  margin-top: 1px;
}

.g-node__task-text {
  flex: 1 1 auto;
  text-align: left;
  /* Soft truncate at 2 lines so long task descriptions don't grow
     the card unbounded. The full text is still visible in the sheet
     when the operator taps the card. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.g-node__task--done {
  text-decoration: line-through;
  opacity: 0.55;
}

.g-node__task--done .g-node__task-marker {
  color: var(--success);
}

.g-node__task--wip .g-node__task-marker {
  color: var(--accent);
}

.g-node__task--more {
  color: var(--text-soft);
  font-style: italic;
  padding-left: 18px;       /* align with task text after the marker */
  font-size: 11px;
}

/* msg 6119: status is now a labelled dot+word pill (see .g-node__chip--dot),
   not an unlabelled left-edge stripe. Cards keep the soft drop-shadow from
   the base .g-node rule. Paused goals stay slightly dimmed. */
.g-node.status-paused { opacity: 0.92; }

/* ---------- Connector lines (CSS-only) ----------
 *
 * The classic CSS family-tree pattern, retuned for the dashboard's
 * palette. Geometry:
 *
 *   1. A vertical drop from the goal node down 22px.
 *   2. A horizontal junction line spanning the children container.
 *   3. A vertical rise from each child node up to the junction.
 *
 * Single-child case collapses to just a vertical line (no horizontal
 * junction); `.g-children.is-single` flag drives that.
 */

.g-children {
  position: relative;
  display: flex;
  flex-wrap: nowrap;
  gap: 18px;
  align-items: flex-start;
  justify-content: center;
  margin-top: 22px;        /* room for the goal-drop line */
  padding-top: 22px;       /* room for the child-rise lines */
}

/* Vertical drop from the goal node */
.g-node--goal::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -22px;
  width: 1.5px;
  height: 22px;
  background: var(--border-strong);
  transform: translateX(-50%);
  pointer-events: none;
}

/* Horizontal junction line across all children (multi-child) */
.g-children:not(.is-single)::before {
  content: "";
  position: absolute;
  top: 0;
  left: calc(0% + 50px);       /* start above first child's centre */
  right: calc(0% + 50px);      /* end above last child's centre */
  height: 1.5px;
  background: var(--border-strong);
  pointer-events: none;
}

/* Vertical rise from each child node up to the junction */
.g-children > .g-node::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -22px;
  width: 1.5px;
  height: 22px;
  background: var(--border-strong);
  transform: translateX(-50%);
  pointer-events: none;
}

/* Mobile: trees stack vertically with a more compact gap. The
   horizontal scroll case (many goals) keeps the same column layout
   but allows scrolling within .goals-map-canvas. */
@media (max-width: 600px) {
  .goals-tree {
    gap: 18px;
  }
  .goal-branch {
    min-width: 180px;
  }
  .g-children {
    gap: 12px;
  }
}

.goals-map-canvas-legacy {
  /* No-op placeholder for the old absolute-positioned canvas style.
     Kept as a guard so a stale build never collides with the new flow. */
  position: relative;
  inset: auto;
}

.goals-map-canvas.fallback-msg {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  color: var(--text-muted);
  font-size: 13.5px;
  line-height: 1.5;
}

.goals-map-legend {
  /* msg 4817 redesign: was an absolute-positioned overlay on the
     Cytoscape canvas. New tree is flow-positioned, so the legend
     now sits below it as a static row inside .goals-map. */
  position: static;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 12px 12px;
  padding: 8px 10px;
  background: color-mix(in srgb, var(--bg-card) 86%, transparent);
  border: 1px solid var(--border);
  border-radius: 12px;
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 500;
}
.legend-chip::before {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-muted);
  border: 1px solid var(--text-muted);
}
.legend-active::before { background: var(--accent); border-color: var(--accent); }
.legend-paused::before { background: transparent; border: 1px dashed var(--text-muted); }
.legend-done::before   { background: transparent; border-color: var(--border-strong); opacity: 0.6; }
.legend-stale::before  { background: var(--warn); border-color: var(--warn); }
.legend-empty::before  { background: var(--alarm); border-color: var(--alarm); }

/* ---------- Node-detail sheet: slides up on tap ---------- */

.goals-node-sheet {
  position: fixed;
  inset: 0;
  z-index: 950;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  pointer-events: none;
}

.goals-node-sheet[hidden] { display: none; }

.goals-node-sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  opacity: 0;
  transition: opacity 0.24s ease;
  pointer-events: auto;
}

.goals-node-sheet.open .goals-node-sheet-backdrop {
  opacity: 1;
}

.goals-node-sheet-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border-top-left-radius: 22px;
  border-top-right-radius: 22px;
  max-height: 78vh;
  /* The panel itself no longer scrolls — only .goals-node-sheet-body does
     (below). This keeps the absolutely-positioned close button and the
     header pinned to the visible top instead of scrolling out of view. */
  overflow: hidden;
  padding: 22px 22px 32px;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
  box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.18);
  pointer-events: auto;
}

.goals-node-sheet.open .goals-node-sheet-panel {
  transform: translateY(0);
}

.goals-node-sheet-panel::before {
  content: "";
  display: block;
  margin: -6px auto 12px;
  width: 36px;
  height: 4px;
  border-radius: 999px;
  background: var(--border-strong);
}

.goals-node-sheet-close {
  position: absolute;
  top: 12px;
  right: 14px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: none;
  background: var(--bg-alt);
  color: var(--text-muted);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
}

.goals-node-sheet-kind {
  display: inline-block;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-soft);
  font-weight: 600;
}

.goals-node-sheet-title {
  margin: 6px 0 10px;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.25;
}

.goals-node-sheet-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}

.goals-node-sheet-body {
  /* The sole scroll region of the sheet (panel is overflow:hidden), so the
     close button + header stay visible no matter how long the content is.
     min-height:0 lets the flex child actually shrink and scroll. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--text);
}

.goals-node-sheet-body p:first-child { margin-top: 0; }
.goals-node-sheet-body p:last-child  { margin-bottom: 0; }

/* feat-054 FR-2 — initiative tasks block, appended to sheet body when
   the tapped node carries a non-empty tasks array. Three states are
   marked with leading glyphs: ○ open, ~ wip, ✓ done. Done tasks
   strikethrough + dim so the live work pops. */
.goals-node-sheet-tasks-heading {
  margin: 18px 0 6px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.goals-node-sheet-tasks {
  list-style: none;
  padding: 0;
  margin: 0;
}
.goals-node-sheet-tasks li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 6px 0;
  font-size: 14px;
  line-height: 1.5;
  border-bottom: 1px dashed var(--border);
}
.goals-node-sheet-tasks li:last-child { border-bottom: none; }
.goals-node-sheet-tasks .task-marker {
  flex: 0 0 16px;
  font-weight: 700;
  color: var(--text-muted);
  font-family: "SF Mono", Menlo, Consolas, monospace;
}
.goals-node-sheet-tasks .task-done .task-marker { color: var(--accent); }
.goals-node-sheet-tasks .task-wip  .task-marker { color: var(--alarm); }
.goals-node-sheet-tasks .task-done .task-text {
  text-decoration: line-through;
  color: var(--text-muted);
}
.goals-node-sheet-tasks .task-text {
  flex: 1 1 auto;
  word-break: break-word;
}

/* ---------- Settings (feat-247 / imp-20260620-19) ----------
   The first real settings screen. One row per declared setting, grouped under
   schema-driven headings; the control matches the setting's type (dropdown,
   slider, toggle, text). Visual language matches the rest of the dashboard. */

.settings-list {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 4px 0 40px;
}

.settings-loading,
.settings-empty {
  color: var(--text-muted);
  padding: 24px 4px;
  text-align: center;
}

.settings-retry {
  margin-left: 8px;
  font: inherit;
  color: var(--accent);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: underline;
}

.settings-readonly-note {
  color: var(--text-muted);
  background: var(--warn-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 0.88rem;
}

.settings-group {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 16px 18px 6px;
}

.settings-group-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 6px;
  color: var(--text);
}

.settings-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  padding: 14px 0;
  border-top: 1px solid var(--border);
}
.settings-group-title + .settings-row { border-top: none; }

.settings-row-head {
  flex: 1 1 240px;
  min-width: 0;
}

.settings-label {
  display: block;
  font-weight: 600;
  color: var(--text);
  font-size: 0.95rem;
}

.settings-desc {
  margin: 3px 0 0;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1.35;
}

.settings-control {
  flex: 0 1 auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* select / text share a base look */
.settings-select,
.settings-text {
  font: inherit;
  color: var(--text);
  background: var(--bg-alt);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  min-width: 180px;
}
.settings-select:focus,
.settings-text:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.settings-select:disabled,
.settings-text:disabled { opacity: 0.55; cursor: not-allowed; }

.settings-hint {
  display: block;
  margin-top: 4px;
  color: var(--text-soft);
  font-size: 0.76rem;
}

/* slider + readout */
.settings-slider-wrap {
  display: flex;
  align-items: center;
  gap: 12px;
}
.settings-slider { width: 160px; accent-color: var(--accent); }
.settings-slider:disabled { opacity: 0.55; cursor: not-allowed; }
.settings-readout {
  min-width: 3ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  font-weight: 600;
}

/* toggle */
.settings-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}
.settings-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.settings-toggle-track {
  width: 44px;
  height: 24px;
  border-radius: 999px;
  background: var(--border-strong);
  position: relative;
  transition: background 0.15s ease;
}
.settings-toggle-track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease;
}
.settings-toggle input:checked + .settings-toggle-track { background: var(--accent); }
.settings-toggle input:checked + .settings-toggle-track::after { transform: translateX(20px); }
.settings-toggle input:disabled + .settings-toggle-track { opacity: 0.55; cursor: not-allowed; }

/* inline validation error (FR-7 / US-3) */
.settings-error {
  flex-basis: 100%;
  color: #ff3b30;
  font-size: 0.82rem;
  margin-top: 2px;
}

/* ---------- Multi-select (feat-199): per-digest source / delivery ---------- */
.settings-checkgroup {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}
.settings-check {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.9rem;
  color: var(--text);
  cursor: pointer;
}
.settings-check input { accent-color: var(--accent); }
.settings-check input:disabled { opacity: 0.55; cursor: not-allowed; }

/* ---------- Connector / channel registry (feat-199 / imp-20260618-79) ---------- */
.connector-row {
  padding: 12px 0;
  border-top: 1px solid var(--border);
}
.settings-group-title + .connector-row { border-top: none; }
.connector-row-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.connector-name {
  font-weight: 600;
  color: var(--text);
  font-size: 0.95rem;
}
.connector-desc {
  margin: 4px 0 0;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1.35;
}
.connector-hint {
  margin: 4px 0 0;
  color: var(--text-soft);
  font-size: 0.78rem;
  line-height: 1.35;
}
.connector-status {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  background: var(--bg-alt);
  white-space: nowrap;
}
.connector-status-ok { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.connector-status-warn { color: #b26a00; border-color: #b26a00; background: rgba(178, 106, 0, 0.10); }
.connector-status-attn { color: #c1121f; border-color: #c1121f; background: rgba(193, 18, 31, 0.10); }
.connector-status-muted { opacity: 0.7; }
.connector-status-unknown { font-style: italic; }

/* ---------- Help section (feat-238 wire-up / imp-20260630-15) ---------- */
/* Follows the Settings tab's card/section conventions so the Help area is
   styled + themed (light/dark/auto) consistently with the rest of the app. */
.help-list {
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 4px 0 40px;
}

.help-group {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: 16px 18px 8px;
}

.help-group-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 6px;
  color: var(--text);
}

.help-item {
  padding: 12px 0;
  border-top: 1px solid var(--border);
}
.help-group-title + .help-item { border-top: none; }

.help-item-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
}

/* Expandable capabilities use a <details>/<summary> disclosure. */
.help-item-expandable > .help-item-summary {
  cursor: pointer;
  list-style: none;
}
.help-item-expandable > .help-item-summary::-webkit-details-marker { display: none; }
.help-item-expandable > .help-item-summary::before {
  content: "▸";
  color: var(--text-soft);
  font-size: 0.8rem;
  transition: transform 0.15s ease;
}
.help-item-expandable[open] > .help-item-summary::before { transform: rotate(90deg); }

.help-name {
  font-family: var(--font-mono, monospace);
  font-weight: 600;
  color: var(--text);
  font-size: 0.95rem;
}

.help-oneliner {
  color: var(--text-muted);
  font-size: 0.86rem;
  line-height: 1.35;
}

.help-item-detail {
  margin: 8px 0 2px;
  padding-left: 16px;
}

.help-howto {
  margin: 0 0 8px;
  color: var(--text);
  font-size: 0.88rem;
  line-height: 1.45;
}

.help-examples {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.help-example {
  margin: 0;
  padding: 7px 10px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-mono, monospace);
  font-size: 0.82rem;
  white-space: pre-wrap;
  word-break: break-word;
}
