diff --git a/client/src/components/cos/ReviewerPicker.jsx b/client/src/components/cos/ReviewerPicker.jsx
index 3c05b6d964..80d254ce7e 100644
--- a/client/src/components/cos/ReviewerPicker.jsx
+++ b/client/src/components/cos/ReviewerPicker.jsx
@@ -26,6 +26,21 @@ const normalizeReviewerValue = (value) => normalizeReviewerSlug(value);
// older build either.
const CUSTOM_MODEL_OPTION = '[custom]';
+// Shared row grid, one template for the header and every row so their columns
+// cannot drift apart. The wide form keeps minimum tracks for order, provider,
+// model, effort, optional, max, and remove — together ~32rem, so the collapse is
+// keyed to a CONTAINER query (`@xl`), not a viewport one: this picker also
+// renders inside a narrow dashboard tile on a wide screen, where a viewport
+// `sm:` was unconditionally true and forced the wide grid into a ~250px column.
+// Below `@xl` a row collapses to a stacked 2-column label/value block, so a
+// narrow container never scrolls horizontally. The header is wide-only — in the
+// stacked form each cell carries its own inline label, since a header far above
+// a stacked row doesn't associate.
+const WIDE_TRACKS = '@xl:grid-cols-[2.5rem_minmax(5rem,1fr)_minmax(8rem,2fr)_minmax(7rem,1fr)_auto_3.25rem_auto]';
+const ROW_CLASS = `grid grid-cols-[auto_1fr] ${WIDE_TRACKS} items-center gap-x-2 gap-y-1 px-1.5 py-1.5 rounded border border-port-border bg-port-bg @xl:border-transparent @xl:bg-transparent @xl:py-0.5 @xl:rounded-none`;
+const CELL_LABEL_CLASS = '@xl:hidden text-[10px] uppercase tracking-wide text-gray-600';
+const HEADER_CLASS = `hidden @xl:grid ${WIDE_TRACKS} items-center gap-x-2 px-1.5 text-[10px] uppercase tracking-wide text-gray-600`;
+
/**
* Ordered multi-reviewer picker, rendered as one row per reviewer with the five
* per-reviewer controls as columns: **Provider | Model | Effort | Optional | Max
@@ -647,18 +662,8 @@ export default function ReviewerPicker({
emit({ reviewers: next });
};
- // Shared row grid. Desktop keeps minimum tracks for order, provider, model,
- // effort, optional, max, and remove; under `sm` it collapses to a stacked
- // 2-column label/value block so a narrow screen never needs horizontal
- // scrolling. The header row is desktop-only — on mobile each cell carries
- // its own inline label, since a header far above a stacked row doesn't
- // associate.
- const ROW_CLASS = 'grid grid-cols-[auto_1fr] sm:grid-cols-[2.5rem_minmax(5rem,1fr)_minmax(8rem,2fr)_minmax(7rem,1fr)_auto_3.25rem_auto] items-center gap-x-2 gap-y-1 px-1.5 py-1.5 rounded border border-port-border bg-port-bg sm:border-transparent sm:bg-transparent sm:py-0.5 sm:rounded-none';
- const CELL_LABEL_CLASS = 'sm:hidden text-[10px] uppercase tracking-wide text-gray-600';
- const HEADER_CLASS = 'hidden sm:grid sm:grid-cols-[2.5rem_minmax(5rem,1fr)_minmax(8rem,2fr)_minmax(7rem,1fr)_auto_3.25rem_auto] items-center gap-x-2 px-1.5 text-[10px] uppercase tracking-wide text-gray-600';
-
return (
-
+
Reviewers (in order):
{selected.length > 0 && (
@@ -672,14 +677,14 @@ export default function ReviewerPicker({
MaxRemove