- {q.total === 0
- ? "No open PRs or ready beads."
- : `${q.actionable} actionable · ${q.total} total${q.stale ? ` · ${q.stale} stale` : ""}`}
- {lastUpdated ? · updated {relativeTime(lastUpdated)} : null}
+ {/* Meta row (queue redesign) — the live summary with a truthful
+ "updated Xm ago" readout, Refresh on the right. The surface title +
+ Tasks/Queue tabs live in the hosting board header (embedded). */}
+
+ {embedded ? null : Queue
}
+
+ {q.total === 0 ? (
+ "No open PRs or ready beads."
+ ) : (
+ <>
+ {q.actionable} actionable
+ ·
+ {q.total} total
+ {q.stale ? (
+ <>
+ ·
+ {q.stale} stale
+ >
+ ) : null}
+ >
+ )}
+ {lastUpdated ? (
+ <>
+ ·updated {relativeTime(lastUpdated)}
+ >
+ ) : null}
-
-
-
+
+ {/* Scope segment — All vs. Unassigned (whole-queue split, unfiltered). */}
+
-
+
{(
[
["all", "All"],
@@ -543,7 +600,7 @@ export function FamiliarWorkQueueView({ familiars = [], onOpenUrl, embedded = fa
@@ -622,6 +679,7 @@ export function FamiliarWorkQueueView({ familiars = [], onOpenUrl, embedded = fa
variant="secondary"
onClick={() => {
setFamiliarFilter(null);
+ setScope("all");
setSearch("");
setPriorityFilter("all");
}}
@@ -651,7 +709,7 @@ export function FamiliarWorkQueueView({ familiars = [], onOpenUrl, embedded = fa
className={`fwq-lane-caret${collapsed ? "" : " is-open"}`}
aria-hidden
/>
-
+
{lane.title}
{lane.items.length}
diff --git a/src/lib/design-token-drift.test.ts b/src/lib/design-token-drift.test.ts
index 8909b9c7a..7745e0e9b 100644
--- a/src/lib/design-token-drift.test.ts
+++ b/src/lib/design-token-drift.test.ts
@@ -43,7 +43,7 @@ import {
// or raise them with an explicit justification in your PR.
const BASELINES = {
offScaleFontSizePx: 154, // 10.5px/11.5px/… — need per-case renormalization to the type scale (banked: canvas-editor.css on the type scale; familiar-tab retokenized the old hero/section styles; research-desk tabs shipped fully on the type scale, -3; familiar-tab five-section rebuild shipped fully on the type scale, -1; projects access page rebuild shipped fully on the type scale, -2)
- offScaleSpacingPx: 1466, // off-4px-grid pad/margin/gap components — banked: canvas-editor.css on the space scale (-1) + familiar-tab snapped to the 4px grid (-5). +123: research-desk redesign (cave-dl74) — the five tab sheets keep the design's compact even 2px-offset rhythm (6/10/14/18/22px gaps and paddings, same family the surface it replaced used); odd values were snapped to the grid in the same PR. +7: home-minimal PR (2026-07-22) — new home-composer/landing-composer.css + hearth-continuations.css compact UI rhythms (7px gaps, 6px accent-dot, 5px pill gap, 13px/14px organic pill padding — off-scale by design for visual compactness). -10: projects access page rebuild (cave-luq4) replaced the hub CSS fully on the space scale; re-banked at 1467 after rebasing onto main commits 7cf9d13f0..fbcadfd3b which added +4 (still -6 vs the pre-rebuild 1473). -1: drive-by banked in the hex-token chunk PR (cave-gyh2)
+ offScaleSpacingPx: 1492, // off-4px-grid pad/margin/gap components — banked: canvas-editor.css on the space scale (-1) + familiar-tab snapped to the 4px grid (-5). +123: research-desk redesign (cave-dl74) — the five tab sheets keep the design's compact even 2px-offset rhythm (6/10/14/18/22px gaps and paddings, same family the surface it replaced used); odd values were snapped to the grid in the same PR. +7: home-minimal PR (2026-07-22) — new home-composer/landing-composer.css + hearth-continuations.css compact UI rhythms (7px gaps, 6px accent-dot, 5px pill gap, 13px/14px organic pill padding — off-scale by design for visual compactness). -10: projects access page rebuild (cave-luq4) replaced the hub CSS fully on the space scale; re-banked at 1467 after rebasing onto main commits 7cf9d13f0..fbcadfd3b which added +4 (still -6 vs the pre-rebuild 1473). -1: drive-by banked in the hex-token chunk PR (cave-gyh2). +26: Queue tab redesign (Queue.dc.html handoff) — the refreshed familiar-work-queue.css keeps the design's compact rhythm (7px control gaps, 6px row-meta gaps, 5px check-dot gaps, 3px accent rail, 30px action-button height, 130px note-textarea min-height, 180px forward-menu min-width, 37px menu offset); on-scale values were tokenized by tokenize-css.mjs in the same PR and font-size literals were snapped to the type scale.
offScaleRadiusPx: 211, // 4px/6px/10px/14px/… radii between the sanctioned steps. +5: research-desk 2px/4px accent-mark radii (short solid marks, not container corners). -5: projects access page rebuild removed the hub's off-step radii
hexOutsideDefinitions: 51, // hex in render CSS (token definitions excluded) — +1: canvas-editor.css sketch-frame #fff ground, sketch CONTENT not interface color, same precedent as chat-canvas.css's thumbnail #fff. -104: cave-gyh2 chunk 1 — dropped 76 stale var(--token, #hex) fallbacks for globally-defined tokens; fixed 6 fallbacks whose token never existed (--warning/--danger -> --color-warning/--color-danger); mapped accent-filled controls to --accent-presence-foreground and danger/success fills to --color-danger-foreground/--color-success-foreground (promoted the success sibling in foundations.css); promoted the codex module's stray #0f1012/#061312 into --cv-* definitions. -1 banked in the design-doc reconcile PR (cave-kf3x). Remaining 51 need design decisions (canvas/QR grounds, GitHub brand colors, profile-card palette, decorative art, #000 shade mixes).
inlineTsxStyles: 227, // style={{…}} in TSX; many are legit dynamic values (banked from 500 — the count had drifted far below the old ceiling; -2: projects access page rebuild dropped the hub's inline styles)
diff --git a/src/styles/familiar-work-queue.css b/src/styles/familiar-work-queue.css
index be7d7a75f..780fd232e 100644
--- a/src/styles/familiar-work-queue.css
+++ b/src/styles/familiar-work-queue.css
@@ -1,6 +1,9 @@
/* Familiar Work Queue (cave-hlv.4) — the beads + PR control tower surface.
- All colour comes from semantic tokens so the surface tracks every theme;
- the lane tones are color-mix tints over the shared status tokens. */
+ Redesigned to the "Tasks list redesign refresh" handoff (Queue.dc.html):
+ a meta row, an All/Unassigned scope segment, a segmented triage toolbar, and
+ full-width grouped rows with a priority/lane accent rail, an inline markdown
+ note composer, and a forward-to-familiar menu. All colour comes from semantic
+ tokens so the surface tracks every theme. */
.fwq {
display: flex;
@@ -12,16 +15,126 @@
container-type: inline-size; /* enables the narrow-surface @container rule */
}
-/* The head is now the shared .surface-compact-header band (globals.css) —
- the old .fwq-head/.fwq-title/.fwq-sub chrome retired with it. */
+/* ── Meta row: live summary + Refresh ─────────────────────────────────────── */
+.fwq-meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-4);
+ padding: var(--space-4) var(--space-6);
+ border-bottom: 1px solid var(--border-hairline);
+}
+.fwq-meta-title {
+ margin: 0;
+ font-size: var(--text-lg, 21px);
+ font-weight: 600;
+ letter-spacing: -0.02em;
+}
+.fwq-meta-summary {
+ margin: 0;
+ flex: 1;
+ min-width: 0;
+ font-size: var(--text-base);
+ color: var(--text-secondary);
+ letter-spacing: 0.005em;
+}
+.fwq-meta-strong {
+ color: var(--text-primary);
+ font-weight: 500;
+}
+.fwq-meta-count {
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-sm);
+ font-variant-numeric: tabular-nums;
+}
+.fwq-meta-sep {
+ color: var(--text-muted);
+ margin: 0 6px;
+}
+.fwq-refresh {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ flex-shrink: 0;
+ padding: 6px var(--space-2);
+ background: transparent;
+ border: 0;
+ color: var(--text-secondary);
+ font-family: inherit;
+ font-size: var(--text-base);
+ cursor: pointer;
+ transition: color 0.12s;
+}
+.fwq-refresh:hover {
+ color: var(--text-primary);
+}
+.fwq-refresh-icon {
+ transition: transform 0.5s ease;
+}
+.fwq-refresh:hover .fwq-refresh-icon {
+ transform: rotate(180deg);
+}
+
+/* ── Segmented controls (scope · priority) ────────────────────────────────── */
+.fwq-scope-row {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: 18px var(--space-6) 0;
+}
+.fwq-seg {
+ display: inline-flex;
+ align-items: stretch;
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+ overflow: hidden;
+}
+.fwq-seg-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ padding: 7px 15px;
+ font-size: var(--text-base);
+ font-weight: 500;
+ line-height: 1;
+ border: 0;
+ cursor: pointer;
+ font-family: inherit;
+ background: transparent;
+ color: var(--text-secondary);
+ transition: background 0.12s, color 0.12s;
+}
+.fwq-seg-btn + .fwq-seg-btn {
+ border-left: 1px solid var(--border-hairline);
+}
+.fwq-seg-btn:hover {
+ color: var(--text-primary);
+}
+.fwq-seg-btn.is-active {
+ background: var(--bg-elevated);
+ color: var(--text-primary);
+}
+.fwq-seg-btn--sm {
+ padding: 6px var(--space-3);
+ font-size: var(--text-sm);
+}
+.fwq-seg-count {
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-xs);
+ font-weight: 600;
+ font-variant-numeric: tabular-nums;
+ color: var(--text-muted);
+}
+.fwq-seg-btn.is-active .fwq-seg-count {
+ color: var(--accent-presence);
+}
/* Per-familiar rollup: filter chips, roving via native buttons. */
.fwq-familiars {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
- padding: var(--space-3) var(--space-5);
- border-bottom: 1px solid var(--border-hairline);
+ padding: 14px var(--space-6) 0;
}
/* Triage toolbar (cave-u2p1): search field + priority bands + sort toggle. */
@@ -29,32 +142,83 @@
display: flex;
flex-wrap: wrap;
align-items: center;
- gap: 6px;
- padding: 10px var(--space-5);
- border-bottom: 1px solid var(--border-hairline);
+ gap: var(--space-3);
+ padding: 18px var(--space-6) 22px;
+}
+.fwq-toolbar-search {
+ flex: 1 1 240px;
+ min-width: 180px;
+}
+.fwq-toolbar-search .ui-search-input {
+ height: 40px;
+ background: var(--bg-sunken);
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+}
+.fwq-toolbar-search .ui-search-input:focus-within {
+ border-color: var(--accent-presence);
+}
+.fwq-toolbar .fwq-seg {
+ height: 40px;
}
-.fwq-toolbar-search { flex: 1 1 240px; min-width: 180px; max-width: 380px; }
-.fwq-toolbar-group { display: inline-flex; gap: var(--space-2); }
-.fwq-toolbar-sort {
+.fwq-sort {
display: inline-flex;
align-items: center;
gap: var(--space-2);
+ height: 40px;
+ padding: 0 13px;
margin-left: auto;
+ background: var(--card);
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+ color: var(--text-secondary);
+ font-family: inherit;
+ font-size: var(--text-base);
+ cursor: pointer;
+ transition: background 0.12s, color 0.12s;
+}
+.fwq-sort:hover {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+
+/* Shared focus ring for the redesigned native controls. */
+.fwq-refresh:focus-visible,
+.fwq-seg-btn:focus-visible,
+.fwq-sort:focus-visible,
+.fwq-act:focus-visible,
+.fwq-forward-trigger:focus-visible,
+.fwq-forward-item:focus-visible,
+.fwq-note-tab:focus-visible,
+.fwq-note-tool:focus-visible,
+.fwq-note-cancel:focus-visible,
+.fwq-note-save:focus-visible {
+ outline: var(--ring-width, 2px) solid var(--ring-focus);
+ outline-offset: 2px;
}
/* Bead titles that open the inspector read as links, not decorations. */
-.fwq-card-name--link {
+.fwq-row-name--link {
min-width: 0;
padding: 0;
border: 0;
color: inherit;
font: inherit;
+ font-size: var(--text-md);
+ font-weight: 450;
+ letter-spacing: -0.005em;
text-align: left;
cursor: pointer;
background: transparent;
border-radius: var(--radius-sm);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.fwq-row-name--link:hover {
+ color: var(--accent-presence);
+ text-decoration: underline;
}
-.fwq-card-name--link:hover { color: var(--accent-presence); text-decoration: underline; }
/* Bead inspector body (rides the shared Modal chrome). */
.fwq-detail { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
@@ -121,7 +285,7 @@
/* ── Attention strip (unlinked / stale PRs — repo hygiene) ───────────────── */
.fwq-attention {
- margin: var(--space-3) var(--space-5) 0;
+ margin: var(--space-3) var(--space-6) 0;
border: 1px solid color-mix(in oklch, var(--color-warning) 34%, transparent);
border-radius: var(--radius-panel, 12px);
background: color-mix(in oklch, var(--color-warning) 7%, transparent);
@@ -193,7 +357,7 @@
connected. Presence-toned so it reads as informational, visually distinct
from the warning-toned "needs attention" strip above it. */
.fwq-asana {
- margin: var(--space-3) var(--space-5) 0;
+ margin: var(--space-3) var(--space-6) 0;
border: 1px solid color-mix(in oklch, var(--accent-presence) 30%, transparent);
border-radius: var(--radius-panel, 12px);
background: color-mix(in oklch, var(--accent-presence) 6%, transparent);
@@ -278,7 +442,7 @@
display: flex;
align-items: center;
gap: var(--space-2);
- margin: var(--space-3) var(--space-5) 0;
+ margin: var(--space-3) var(--space-6) 0;
padding: 7px var(--space-3);
border-radius: var(--radius-panel, 12px);
font-size: var(--text-sm);
@@ -306,50 +470,77 @@
flex: 1;
min-height: 0;
overflow-y: auto;
- padding: var(--space-4) var(--space-5) var(--space-8);
+ padding: 2px var(--space-6) var(--space-10);
display: flex;
flex-direction: column;
- gap: 18px;
+ gap: var(--space-2);
}
-/* ── Lanes ──────────────────────────────────────────────────────────────── */
+/* ── Groups (lanes) ───────────────────────────────────────────────────────── */
.fwq-lane {
- border: 1px solid var(--border-hairline);
- border-radius: var(--radius-panel, 12px);
- background: var(--bg-raised, var(--bg-elevated));
- overflow: hidden;
+ --fwq-accent: var(--border-strong);
}
+.fwq-lane--urgent { --fwq-accent: var(--color-warning); }
+.fwq-lane--ready { --fwq-accent: var(--color-success); }
+.fwq-lane--neutral { --fwq-accent: var(--accent-presence); }
+.fwq-lane--quiet { --fwq-accent: var(--border-strong); }
.fwq-lane-head {
- display: flex;
- align-items: center;
- border-bottom: 1px solid var(--border-hairline);
- font-size: var(--text-sm);
- font-weight: 600;
+ display: block;
}
/* The whole head row is the disclosure control (cave-19jy). */
.fwq-lane-toggle {
display: flex;
- flex: 1;
- min-width: 0;
+ width: 100%;
align-items: center;
- gap: var(--space-2);
- padding: 9px var(--space-3);
+ gap: 11px;
+ padding: var(--space-3) var(--space-3) var(--space-3) 14px;
border: 0;
+ border-top: 1px solid var(--border-hairline);
+ border-bottom: 1px solid var(--border-hairline);
+ border-left: 2px solid var(--fwq-accent);
color: inherit;
font: inherit;
text-align: left;
cursor: pointer;
- background: transparent;
+ background: var(--bg-elevated);
+ transition: background 0.12s;
}
-.fwq-lane-toggle:hover .fwq-lane-title { color: var(--text-primary); }
+.fwq-lane-toggle:hover { background: var(--bg-hover); }
.fwq-lane-caret {
flex-shrink: 0;
- color: var(--text-muted);
+ color: var(--text-secondary);
transition: transform var(--duration-fast, 120ms) ease;
}
.fwq-lane-caret.is-open { transform: rotate(90deg); }
+.fwq-lane-icon {
+ flex-shrink: 0;
+ color: var(--fwq-accent);
+}
+.fwq-lane--quiet .fwq-lane-icon { color: var(--text-secondary); }
+
+.fwq-lane-title {
+ flex: 1;
+ min-width: 0;
+ font-size: var(--text-xs);
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--text-primary);
+}
+
+.fwq-lane-count {
+ flex-shrink: 0;
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-xs);
+ font-weight: 600;
+ font-variant-numeric: tabular-nums;
+ color: var(--text-muted);
+ padding: 3px 9px;
+ border-radius: var(--radius-control, 6px);
+ background: var(--bg-sunken);
+}
/* Show all N / Show top N — quiet full-width foot row on capped lanes. */
.fwq-lane-more {
@@ -367,108 +558,185 @@
}
.fwq-lane-more:hover { color: var(--accent-presence); background: var(--bg-hover); }
-/* Priority tint — P0/P1 must pop in a mixed lane; P2+ stays quiet. */
-.fwq-tag--p0,
-.fwq-tag--p1 {
- color: var(--color-warning);
- border-color: color-mix(in oklch, var(--color-warning) 38%, var(--border-hairline));
- background: color-mix(in oklch, var(--color-warning) 10%, transparent);
-}
-.fwq-tag--p0 {
- color: var(--color-danger);
- border-color: color-mix(in oklch, var(--color-danger) 38%, var(--border-hairline));
- background: color-mix(in oklch, var(--color-danger) 10%, transparent);
-}
-
-.fwq-lane-title {
- flex: 1;
- min-width: 0;
-}
-
-.fwq-lane-count {
- font-variant-numeric: tabular-nums;
- color: var(--text-muted);
- padding: 1px var(--space-2);
- border-radius: var(--radius-pill);
- background: var(--bg-hover);
-}
-
-/* Lane tones: a thin accent strip + tinted header, driven by status tokens. */
-.fwq-lane--urgent .fwq-lane-head {
- color: var(--color-warning);
- background: color-mix(in oklch, var(--color-warning) 8%, transparent);
-}
-.fwq-lane--ready .fwq-lane-head {
- color: var(--color-success);
- background: color-mix(in oklch, var(--color-success) 8%, transparent);
-}
-.fwq-lane--neutral .fwq-lane-head {
- color: var(--accent-presence);
- background: color-mix(in oklch, var(--accent-presence) 8%, transparent);
-}
-.fwq-lane--quiet .fwq-lane-head {
- color: var(--text-muted);
-}
-
.fwq-cards {
list-style: none;
margin: 0;
padding: 0;
}
-/* ── Cards ──────────────────────────────────────────────────────────────── */
-.fwq-card {
- display: flex;
- flex-wrap: wrap;
+/* ── Rows ─────────────────────────────────────────────────────────────────── */
+.fwq-row {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
- justify-content: space-between;
- gap: var(--space-3);
- padding: 10px var(--space-3);
+ column-gap: var(--space-5);
+ padding: 13px var(--space-5);
border-bottom: 1px solid var(--border-hairline);
+ box-shadow: inset 3px 0 0 var(--fwq-rail, transparent);
+ transition: background 0.12s, box-shadow 0.12s;
+}
+/* Rail tone (set by railClass()): priority for bead rows, lane for PR rows. */
+.fwq-row--rail-danger { --fwq-rail: var(--color-danger); }
+.fwq-row--rail-warning { --fwq-rail: var(--color-warning); }
+.fwq-row--rail-success { --fwq-rail: var(--color-success); }
+.fwq-row--rail-neutral { --fwq-rail: color-mix(in oklch, var(--accent-presence) 42%, transparent); }
+.fwq-row--rail-waiting { --fwq-rail: color-mix(in oklch, var(--color-warning) 55%, transparent); }
+.fwq-row:hover {
+ background: var(--bg-hover);
}
-
-.fwq-card:last-child {
- border-bottom: 0;
-}
-
-.fwq-card.is-stale {
+.fwq-row.is-stale {
background: color-mix(in oklch, var(--color-warning) 5%, transparent);
}
-.fwq-card-main {
+.fwq-row-main {
min-width: 0;
- flex: 1;
}
-.fwq-card-title {
+.fwq-row-title {
display: flex;
align-items: baseline;
- gap: 7px;
+ gap: var(--space-2);
min-width: 0;
}
.fwq-pr-num {
- font: 600 12px/1 var(--font-mono);
+ font: 600 12.5px/1 var(--font-mono);
color: var(--text-muted);
flex-shrink: 0;
}
-.fwq-card-name {
- font-size: var(--text-base);
+.fwq-row-name {
+ flex: 1;
+ min-width: 0;
+ font-size: var(--text-md);
+ font-weight: 450;
+ letter-spacing: -0.005em;
+ line-height: 1.3;
color: var(--text-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
-.fwq-card-meta {
+.fwq-row-meta {
display: flex;
+ align-items: center;
+ gap: 9px;
flex-wrap: wrap;
- gap: 5px;
- margin-top: 5px;
+ margin-top: 9px;
+}
+
+/* Assignment pill + sigil. Unassigned reads in presence tint (act on it);
+ an owned bead reads in success (already taken). */
+.fwq-assign {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ height: 20px;
+ padding: 0 var(--space-2);
+ font-size: var(--text-xs);
+ font-weight: 500;
+ border-radius: var(--radius-pill);
+ white-space: nowrap;
+ color: var(--accent-presence);
+ background: color-mix(in oklch, var(--accent-presence) 12%, transparent);
+ border: 1px solid color-mix(in oklch, var(--accent-presence) 26%, transparent);
+}
+.fwq-assign--claimed {
+ color: var(--color-success);
+ background: color-mix(in oklch, var(--color-success) 14%, transparent);
+ border-color: color-mix(in oklch, var(--color-success) 32%, transparent);
+}
+.fwq-sigil {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ border: 1.5px solid currentColor;
+ box-sizing: border-box;
+ flex-shrink: 0;
+}
+.fwq-sigil--filled {
+ border: 0;
+ background: currentColor;
+}
+
+.fwq-bead {
+ display: inline-flex;
+ align-items: center;
+ height: 20px;
+ padding: 0 7px;
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-xs);
+ color: var(--text-secondary);
+ background: var(--bg-sunken);
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+ white-space: nowrap;
}
-/* Tag recipe (§ design language): 6–18% fill, 30–45% border, solid text. */
+/* PR check state as a dot-pill (design's "checks pending"). */
+.fwq-checks {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ height: 20px;
+ padding: 0 var(--space-2);
+ font-size: var(--text-xs);
+ border-radius: var(--radius-pill);
+ white-space: nowrap;
+}
+.fwq-checks-dot {
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: currentColor;
+}
+.fwq-checks--pending {
+ color: var(--color-warning);
+ background: color-mix(in oklch, var(--color-warning) 13%, transparent);
+ border: 1px solid color-mix(in oklch, var(--color-warning) 32%, transparent);
+}
+.fwq-checks--failing {
+ color: var(--color-danger);
+ background: color-mix(in oklch, var(--color-danger) 13%, transparent);
+ border: 1px solid color-mix(in oklch, var(--color-danger) 32%, transparent);
+}
+.fwq-checks--passing {
+ color: var(--color-success);
+ background: color-mix(in oklch, var(--color-success) 13%, transparent);
+ border: 1px solid color-mix(in oklch, var(--color-success) 32%, transparent);
+}
+.fwq-checks--unknown {
+ color: var(--text-muted);
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-hairline);
+}
+
+.fwq-row-trailing {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: var(--text-sm);
+ color: var(--text-muted);
+}
+.fwq-pri-text {
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-xs);
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ color: var(--text-secondary);
+}
+.fwq-pri-text--p0 { color: var(--color-danger); }
+.fwq-pri-text--p1 { color: var(--color-warning); }
+.fwq-dot-sep { opacity: 0.55; }
+.fwq-updated {
+ font-variant-numeric: tabular-nums;
+ white-space: nowrap;
+}
+
+/* Tag recipe (§ design language): 6–18% fill, 30–45% border, solid text.
+ Kept for the row's review-decision / merge-eligible / stale pills and the
+ bead inspector's status chips. */
.fwq-tag {
font-size: var(--text-xs);
line-height: 1.5;
@@ -478,47 +746,41 @@
color: var(--text-muted);
white-space: nowrap;
}
-
.fwq-tag--familiar {
border-color: color-mix(in oklch, var(--accent-presence) 32%, transparent);
background: color-mix(in oklch, var(--accent-presence) 10%, transparent);
color: var(--accent-presence);
}
-
-.fwq-tag--bead {
- font-family: var(--font-mono);
-}
-
-.fwq-tag--check-passing {
- color: var(--color-success);
- border-color: color-mix(in oklch, var(--color-success) 30%, transparent);
+.fwq-tag--bead { font-family: var(--font-mono); }
+.fwq-tag--p0,
+.fwq-tag--p1 {
+ color: var(--color-warning);
+ border-color: color-mix(in oklch, var(--color-warning) 38%, var(--border-hairline));
+ background: color-mix(in oklch, var(--color-warning) 10%, transparent);
}
-
-.fwq-tag--check-failing {
+.fwq-tag--p0 {
color: var(--color-danger);
- border-color: color-mix(in oklch, var(--color-danger) 34%, transparent);
- background: color-mix(in oklch, var(--color-danger) 8%, transparent);
-}
-
-.fwq-tag--check-pending {
- color: var(--color-warning);
- border-color: color-mix(in oklch, var(--color-warning) 30%, transparent);
+ border-color: color-mix(in oklch, var(--color-danger) 38%, var(--border-hairline));
+ background: color-mix(in oklch, var(--color-danger) 10%, transparent);
}
-
.fwq-tag--ready {
color: var(--color-success);
border-color: color-mix(in oklch, var(--color-success) 34%, transparent);
background: color-mix(in oklch, var(--color-success) 8%, transparent);
}
-
.fwq-tag--stale {
color: var(--color-warning);
border-color: color-mix(in oklch, var(--color-warning) 40%, transparent);
background: color-mix(in oklch, var(--color-warning) 10%, transparent);
}
-/* Quiet per-item age ("updated 2h ago" / "merged 1h ago") — plain muted text,
- not a pill, so it reads as metadata rather than a status. */
+/* The bead inspector reuses these meta/time classes. */
+.fwq-card-meta {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 5px;
+}
.fwq-card-time {
font-size: var(--text-xs);
line-height: 1.5;
@@ -527,86 +789,322 @@
font-variant-numeric: tabular-nums;
}
-.fwq-card-actions {
+/* ── Row actions ──────────────────────────────────────────────────────────── */
+.fwq-row-actions {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
-/* Claim-for-familiar picker (cave-p63a): a compact ghost control sitting
- beside the xs Claim button, matching its 22px height. */
-.fwq-claim-for {
- min-height: 22px;
- padding: 0 var(--space-2);
+.fwq-act {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ height: 30px;
+ padding: 0 var(--space-3);
+ font-family: inherit;
+ font-size: var(--text-sm);
+ font-weight: 500;
+ color: var(--text-secondary);
+ background: var(--card);
border: 1px solid var(--border-hairline);
- border-radius: var(--radius-control, 8px);
- background: transparent;
- color: var(--text-muted);
- font-size: var(--text-xs);
- gap: var(--space-1);
+ border-radius: var(--radius-control, 6px);
+ cursor: pointer;
+ white-space: nowrap;
+ transition: background 0.12s, color 0.12s, border-color 0.12s;
+}
+.fwq-act:hover:not(:disabled) {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+.fwq-act.is-active {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+.fwq-act:disabled {
+ opacity: 0.55;
+ cursor: default;
+}
+.fwq-act--claim {
+ color: var(--text-primary);
+}
+.fwq-act--claim:hover:not(:disabled) {
+ color: var(--accent-presence);
+ background: color-mix(in oklch, var(--accent-presence) 14%, transparent);
+ border-color: color-mix(in oklch, var(--accent-presence) 42%, transparent);
}
-.fwq-claim-for:hover {
+/* ── Forward-to-familiar menu (claim on a familiar's behalf) ──────────────── */
+.fwq-forward {
+ position: relative;
+}
+.fwq-forward-scrim {
+ position: fixed;
+ inset: 0;
+ z-index: 20;
+ background: transparent;
+ border: 0;
+ padding: 0;
+ cursor: default;
+}
+.fwq-forward-trigger {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ height: 30px;
+ padding: 0 10px;
+ font-family: inherit;
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
+ background: var(--card);
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+ cursor: pointer;
+ transition: background 0.12s, color 0.12s, border-color 0.12s;
+}
+.fwq-forward-trigger:hover:not(:disabled) {
+ background: var(--bg-hover);
color: var(--text-primary);
+}
+.fwq-forward-trigger.is-open {
+ position: relative;
+ z-index: 31;
+ border-color: var(--border-strong);
+ color: var(--text-primary);
+}
+.fwq-forward-trigger:disabled {
+ opacity: 0.55;
+ cursor: default;
+}
+.fwq-forward-caret {
+ transition: transform 0.15s;
+}
+.fwq-forward-trigger.is-open .fwq-forward-caret {
+ transform: rotate(180deg);
+}
+.fwq-forward-menu {
+ position: absolute;
+ top: 37px;
+ right: 0;
+ z-index: 30;
+ min-width: 180px;
+ padding: 5px;
+ background: var(--popover);
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-panel, 8px);
+ box-shadow: 0 10px 30px -8px rgba(0, 0, 0, 0.55);
+}
+.fwq-forward-menu-head {
+ margin: 0;
+ padding: 6px var(--space-2) 5px;
+ font-size: var(--text-2xs);
+ font-weight: 600;
+ letter-spacing: 0.09em;
+ text-transform: uppercase;
+ color: var(--text-muted);
+}
+.fwq-forward-item {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ width: 100%;
+ height: 32px;
+ padding: 0 var(--space-2);
+ background: transparent;
+ border: 0;
+ border-radius: var(--radius-control, 6px);
+ cursor: pointer;
+ font-family: inherit;
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
+ transition: background 0.12s, color 0.12s;
+}
+.fwq-forward-item:hover {
background: var(--bg-hover);
+ color: var(--text-primary);
+}
+.fwq-forward-avatar {
+ width: 19px;
+ height: 19px;
+ border-radius: var(--radius-control, 6px);
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-size: var(--text-2xs);
+ font-weight: 700;
+ color: var(--accent-presence);
+ background: color-mix(in oklch, var(--accent-presence) 20%, transparent);
+}
+.fwq-forward-name {
+ flex: 1;
+ text-align: left;
}
-/* Handoff-note affordances (cave-hlv.2): a full-width hint + inline composer
- that wrap below the card's title/actions row. */
-.fwq-card-hint {
- flex: 0 0 100%;
- margin: 2px 0 0;
- font-size: var(--text-xs);
+/* ── Inline markdown note composer ────────────────────────────────────────── */
+.fwq-row-hint {
+ grid-column: 1 / -1;
+ margin: var(--space-2) 0 0;
+ font-size: var(--text-sm);
color: var(--text-muted);
}
.fwq-note {
- flex: 0 0 100%;
+ grid-column: 1 / -1;
+ margin-top: 14px;
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-panel, 8px);
+ background: var(--bg-sunken);
+ overflow: hidden;
+}
+.fwq-note-head {
display: flex;
- flex-direction: column;
- gap: 6px;
- margin-top: var(--space-2);
+ align-items: center;
+ gap: 10px;
+ padding: var(--space-2) 10px;
+ border-bottom: 1px solid var(--border-hairline);
+}
+.fwq-note-tabs {
+ display: inline-flex;
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+ overflow: hidden;
+}
+.fwq-note-tab {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 26px;
+ padding: 0 var(--space-3);
+ font-family: inherit;
+ font-size: var(--text-sm);
+ font-weight: 500;
+ border: 0;
+ cursor: pointer;
+ background: transparent;
+ color: var(--text-secondary);
+ transition: background 0.12s, color 0.12s;
+}
+.fwq-note-tab + .fwq-note-tab {
+ border-left: 1px solid var(--border-hairline);
+}
+.fwq-note-tab.is-active {
+ background: var(--bg-elevated);
+ color: var(--text-primary);
+}
+.fwq-note-tools {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+ padding-left: var(--space-1);
+ border-left: 1px solid var(--border-hairline);
+}
+.fwq-note-tool {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 26px;
+ min-width: 26px;
+ padding: 0 6px;
+ background: transparent;
+ border: 0;
+ border-radius: var(--radius-control, 6px);
+ color: var(--text-secondary);
+ font-family: inherit;
+ font-size: var(--text-sm);
+ cursor: pointer;
+ transition: background 0.12s, color 0.12s;
+}
+.fwq-note-tool:hover {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+.fwq-note-tool--b { font-weight: 700; }
+.fwq-note-tool--i { font-style: italic; }
+.fwq-note-tool--h { font-weight: 700; }
+.fwq-note-lang {
+ margin-left: auto;
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-2xs);
+ letter-spacing: 0.04em;
+ color: var(--text-muted);
+}
+.fwq-note-body {
+ padding: var(--space-3) 14px;
}
-
.fwq-note-input {
+ display: block;
width: 100%;
- min-height: 44px;
+ min-height: 130px;
resize: vertical;
- padding: 7px 9px;
- border-radius: var(--radius-control, 8px);
- border: 1px solid var(--border-hairline);
- background: var(--bg-base);
+ background: transparent;
+ border: 0;
+ outline: none;
color: var(--text-primary);
- font: inherit;
- font-size: var(--text-sm);
- line-height: 1.4;
+ font-family: var(--font-mono), monospace;
+ font-size: var(--text-base);
+ line-height: 1.65;
}
-
-.fwq-note-input:focus-visible {
- outline: 2px solid var(--ring-focus, var(--accent-presence));
- outline-offset: 1px;
+.fwq-note-preview {
+ min-height: 130px;
+ font-size: var(--text-base);
+ color: var(--text-primary);
}
-
-.fwq-note-actions {
+.fwq-note-preview a { color: var(--accent-presence); }
+.fwq-note-foot {
display: flex;
+ align-items: center;
justify-content: flex-end;
- gap: 6px;
+ gap: var(--space-2);
+ padding: 9px var(--space-3);
+ border-top: 1px solid var(--border-hairline);
+}
+.fwq-note-cancel {
+ height: 30px;
+ padding: 0 13px;
+ background: transparent;
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-control, 6px);
+ color: var(--text-secondary);
+ font-family: inherit;
+ font-size: var(--text-sm);
+ cursor: pointer;
+ transition: background 0.12s, color 0.12s;
+}
+.fwq-note-cancel:hover:not(:disabled) {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+.fwq-note-save {
+ height: 30px;
+ padding: 0 15px;
+ background: var(--primary);
+ border: 0;
+ border-radius: var(--radius-control, 6px);
+ color: var(--primary-foreground);
+ font-family: inherit;
+ font-size: var(--text-sm);
+ font-weight: 600;
+ cursor: pointer;
+ transition: filter 0.12s;
+}
+.fwq-note-save:hover:not(:disabled) {
+ filter: brightness(1.08);
+}
+.fwq-note-save:disabled,
+.fwq-note-cancel:disabled {
+ opacity: 0.55;
+ cursor: default;
}
-/* Narrow surface (split tile / mobile): stack card body over actions. */
+/* Narrow surface (split tile / mobile): stack row body over actions. */
@container (max-width: 560px) {
- .fwq-card {
- flex-direction: column;
- align-items: stretch;
+ .fwq-row {
+ grid-template-columns: 1fr;
+ row-gap: var(--space-3);
}
- .fwq-card-actions {
+ .fwq-row-actions {
justify-content: flex-end;
}
- /* Column flow already stacks children full-width; the row-mode 100% basis
- would otherwise resolve against the (vertical) main axis. */
- .fwq-card-hint,
- .fwq-note {
- flex: 0 0 auto;
- }
}
diff --git a/tests/familiar-work-queue.spec.ts b/tests/familiar-work-queue.spec.ts
index accc24fc4..68349cdb6 100644
--- a/tests/familiar-work-queue.spec.ts
+++ b/tests/familiar-work-queue.spec.ts
@@ -180,7 +180,7 @@ test.describe("familiar work queue (PR control tower)", () => {
await expect(noteToggle).toBeFocused();
await noteToggle.click();
await cleanup.getByRole("textbox", { name: /Handoff note for cave-open/ }).fill("Verified: lanes render, close gated.");
- await cleanup.getByRole("button", { name: "Add note" }).click();
+ await cleanup.getByRole("button", { name: "Save note" }).click();
await expect(noteToggle).toBeFocused();
// The note posts as a comment on the bead…