/*
 * ds/feedback.css — state chips, callouts, the gate card, empty/error/stale
 * states, progress, toasts.
 *
 * This is where Phase 2's eight states and three risk tiers become visible. The
 * governing rule is P5: every state the system can occupy has a designed
 * appearance, including the three the old palette had no colour for -- waiting on
 * a person, stale, and cannot-tell.
 */

/* =========================== 1. Chips ============================= */
.ds-chip {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    padding: 2px var(--sp-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-full);
    background: var(--sunken);
    color: var(--soft);
    font-size: var(--fs-micro);
    font-weight: var(--fw-medium);
    line-height: 1.5;
    white-space: nowrap;
}
.ds-chip-dot {
    width: 6px; height: 6px; flex: none;
    border-radius: var(--radius-full);
    background: currentColor;
}

.ds-chip--pending { color: var(--st-pending-ink); background: var(--st-pending-bg); border-color: var(--st-pending-line); }
.ds-chip--running { color: var(--st-running-ink); background: var(--st-running-bg); border-color: var(--st-running-line); }
.ds-chip--blocked { color: var(--st-blocked-ink); background: var(--st-blocked-bg); border-color: var(--st-blocked-line); }
.ds-chip--waiting { color: var(--st-waiting-ink); background: var(--st-waiting-bg); border-color: var(--st-waiting-line); }
.ds-chip--passed  { color: var(--st-passed-ink);  background: var(--st-passed-bg);  border-color: var(--st-passed-line); }
.ds-chip--failed  { color: var(--st-failed-ink);  background: var(--st-failed-bg);  border-color: var(--st-failed-line); }
.ds-chip--stale   { color: var(--st-stale-ink);   background: var(--st-stale-bg);   border-color: var(--st-stale-line); }

/* "Cannot tell" gets a DASHED border. A solid chip reads as a settled verdict;
   this one has to read as an open question, because that is what it is -- and
   this product is in that state constantly. */
.ds-chip--unknown {
    color: var(--st-unknown-ink);
    background: var(--st-unknown-bg);
    border-color: var(--st-unknown-line);
    border-style: dashed;
}

.ds-chip--low    { color: var(--risk-low-ink);    background: var(--risk-low-bg);    border-color: var(--risk-low-line); }
.ds-chip--medium { color: var(--risk-medium-ink); background: var(--risk-medium-bg); border-color: var(--risk-medium-line); }
.ds-chip--high   { color: var(--risk-high-ink);   background: var(--risk-high-bg);   border-color: var(--risk-high-line); }

/* A running chip pulses. The only animation in the state set, because "something
   is happening right now" is the only state where the screen is lying if it
   looks static. */
.ds-chip--running .ds-chip-dot { animation: ds-pulse 1.6s var(--ease-in-out) infinite; }
@keyframes ds-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) {
    .ds-chip--running .ds-chip-dot { animation: none; }
}

.ds-chip--square { border-radius: var(--radius-xs); font-family: var(--font-mono); }

/* A CHIP THAT IS A CONTROL. The chip was read-only everywhere until Knowledge needed to
   filter a corpus by document kind, and a filter row made of read-only chips gives no
   sign that any of them can be pressed. The state is `aria-selected`, which is also what
   makes it announce correctly -- the old version carried an `is-active` class as well,
   set in the same line, so one fact had two mechanisms. */
.ds-chip--pick { cursor: pointer; }
.ds-chip--pick:hover { border-color: var(--line-firm); color: var(--ink); }
.ds-chip--pick[aria-selected="true"] {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent-text);
}

/* =========================== 2. Callouts ========================== */
.ds-note {
    display: flex; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--line);
    border-left-width: 3px;
    border-radius: var(--radius-sm);
    background: var(--panel-bg);
    font-size: var(--fs-dense);
    color: var(--soft);
}
.ds-note-icon { flex: none; margin-top: 1px; }
.ds-note-body { min-width: 0; }
.ds-note-title { font-weight: var(--fw-semibold); color: var(--ink); margin-bottom: var(--sp-1); }

.ds-note--info { border-left-color: var(--info);             background: var(--info-soft); }
.ds-note--warn { border-left-color: var(--st-waiting-ink);   background: var(--st-waiting-bg); }
.ds-note--fail { border-left-color: var(--fail);             background: var(--st-failed-bg); }
.ds-note--pass { border-left-color: var(--pass);             background: var(--st-passed-bg); }
.ds-note--stale{ border-left-color: var(--st-stale-ink);     background: var(--st-stale-bg); }

/* =========================== 3. Cards ============================ */
.ds-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    padding: var(--sp-4);
    min-width: 0;
}
.ds-card--flush { padding: 0; overflow: hidden; }
.ds-card-head {
    display: flex; align-items: center; gap: var(--sp-2);
    padding-bottom: var(--sp-3); margin-bottom: var(--sp-3);
    border-bottom: 1px solid var(--line);
}
.ds-card-title { font-size: var(--fs-lead); font-weight: var(--fw-semibold); min-width: 0; }
.ds-card-head > .ds-card-actions { margin-left: auto; display: flex; gap: var(--sp-2); flex: none; }

/* A card whose left edge carries a state. Cheaper than a chip when the whole
   card IS the state. */
.ds-card--blocked { border-left: 3px solid var(--st-blocked-ink); }
.ds-card--waiting { border-left: 3px solid var(--st-waiting-ink); }
.ds-card--running { border-left: 3px solid var(--st-running-ink); }
.ds-card--failed  { border-left: 3px solid var(--st-failed-ink); }

/* =========================== 4. The gate card ===================== */
/*
 * The signature component. Phase 0's automation-bias finding: explanations made
 * oversight WORSE -- +19 points of deference, +5 more with narrative rationales,
 * and intervention success of 9-26%. So this card does not lead with reasoning.
 * It leads with scope and with what was NOT examined, and it folds the prose.
 * `ds-gate-gaps` is deliberately the same visual weight as `ds-gate-scope`.
 */
.ds-gate {
    border: 1px solid var(--line-firm);
    border-radius: var(--radius-md);
    background: var(--surface);
    overflow: hidden;
}
.ds-gate--high { border-color: var(--st-failed-line); }

/*
 * THE CARD THE WHOLE SCREEN IS FOR. It read as terms and conditions -- a bordered box
 * at the bottom of a page, its heading the same size as the five card headings above
 * it, its contents two labelled rows of grey prose. Everything on the screen is a step
 * towards it, and nothing about it said so.
 *
 * One accent edge and one heading a size larger than any card on the page. Visual
 * weight is a claim about importance, and this is the only element on this screen
 * where the claim is true, so it is the only one that makes it.
 */
.ds-gate--prime {
    border-color: var(--accent-line, var(--line-firm));
    border-left: 3px solid var(--accent-fill);
    box-shadow: var(--shadow-md);
}
.ds-gate--prime .ds-gate-head { background: var(--accent-soft); border-bottom-color: var(--accent-line, var(--line)); }
.ds-gate--prime .ds-gate-title { font-size: var(--fs-h3); }
.ds-gate--prime .ds-gate-foot { padding: var(--sp-4); }

/*
 * THE SAME CARD, WHEN IT IS OFF SCREEN. Six rows and six reports is more than a
 * windowful, so the card that says what has been decided spends most of a session
 * below the fold.
 *
 * It carries a count and a jump, never a build button: an irreversible action
 * floating over the page, pressable without the scope and the gaps on screen, is the
 * dialog this page removed, rebuilt at the bottom of the window.
 */
.ds-stickybar {
    position: fixed; left: var(--ds-rail-w); right: 0; bottom: 0;
    z-index: var(--z-popover);
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-6);
    background: var(--raised);
    border-top: 1px solid var(--line-firm);
    box-shadow: 0 -4px 16px rgb(0 0 0 / 0.06);
    animation: ds-slide-up var(--dur-base) var(--ease-out);
}
@keyframes ds-slide-up { from { transform: translateY(100%); } }
.ds-stickybar[hidden] { display: none !important; }
.ds-stickybar-text { font-size: var(--fs-dense); font-weight: var(--fw-medium); min-width: 0; }
.ds-stickybar > .ds-btn:last-child { margin-left: auto; }
@media (max-width: 1024px) { .ds-stickybar { left: var(--ds-rail-w-min); } }
@media (max-width: 680px)  { .ds-stickybar { left: 0; padding: var(--sp-3) var(--sp-4); } }

/* ------------------------- The conversation ------------------------- *
 * A panel over the report, opened from a button at its foot. It was the last card in
 * the report's scroll, which put the one place a reader can push back five screens
 * below the claim they want to push back on -- and reachable only by scrolling past
 * the thing that prompted the question.
 */
.ds-fab {
    position: absolute; right: var(--sp-5); bottom: calc(var(--sp-5) + 48px);
    display: inline-flex; align-items: center; gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-4);
    background: var(--accent-fill);
    color: var(--on-accent);
    border-radius: var(--radius-full);
    box-shadow: var(--shadow-lg);
    font-size: var(--fs-dense); font-weight: var(--fw-medium);
    z-index: 2;
}
.ds-fab:hover { text-decoration: none; filter: brightness(1.06); }
.ds-fab-count {
    font-size: var(--fs-micro); font-variant-numeric: tabular-nums;
    background: rgb(255 255 255 / 0.25);
    border-radius: var(--radius-full);
    padding: 0 var(--sp-1-5); min-width: 18px; text-align: center;
}
.ds-fab-count[hidden] { display: none !important; }

/* TWO CAPS FIGHTING, AND THE SMALLER ONE WON.
 * This capped the panel at 70% of the drawer and `.ds-chatlog` capped ITSELF at 340px
 * -- so the conversation got 340px however tall the drawer was, and the panel left the
 * rest of its own height empty above a window three messages fit in. "I'm not sure why
 * this height is so small, whereas we have so much space above it."
 * A HEIGHT, not only a maximum: the log flexes to fill it (see `.ds-chatlog` in
 * data.css), so the panel is as tall as the drawer allows and the log is as tall as
 * the panel. */
.ds-chatpanel {
    position: absolute; right: var(--sp-4); bottom: calc(var(--sp-4) + 48px);
    width: min(480px, calc(100% - var(--sp-8)));
    height: min(72%, 620px);
    max-height: calc(100% - var(--sp-4) - 56px);
    min-height: 260px;
    display: flex; flex-direction: column;
    background: var(--raised);
    border: 1px solid var(--line-firm);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xl);
    z-index: 3;
    animation: ds-pop var(--dur-fast) var(--ease-out);
}
.ds-chatpanel[hidden] { display: none !important; }
.ds-chatpanel-head {
    display: flex; align-items: center; gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--line);
    flex: none; min-width: 0;
}
.ds-chatpanel-head > .ds-iconbtn { margin-left: auto; flex: none; }
.ds-chatpanel > .ds-chatlog { flex: 1 1 auto; min-height: 120px; overflow-y: auto; padding: var(--sp-3) var(--sp-4); }
.ds-chatpanel > .ds-chatform { flex: none; padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line); }

.ds-gate-head {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    background: var(--panel-bg);
    border-bottom: 1px solid var(--line);
}
.ds-gate-title { font-size: var(--fs-lead); font-weight: var(--fw-semibold); min-width: 0; }
.ds-gate-head > .ds-chip { margin-left: auto; flex: none; }

.ds-gate-body { display: grid; grid-template-columns: max-content minmax(0, 1fr); }
.ds-gate-row { display: contents; }
.ds-gate-k {
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
    font-size: var(--fs-micro);
    font-weight: var(--fw-semibold);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--mute);
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}
.ds-gate-v {
    padding: var(--sp-3) var(--sp-4) var(--sp-3) 0;
    font-size: var(--fs-dense);
    border-bottom: 1px solid var(--line);
    min-width: 0;
}
.ds-gate-row:last-of-type .ds-gate-k,
.ds-gate-row:last-of-type .ds-gate-v { border-bottom: 0; }

/* Not examined. Same weight as the scope row; tinted so it cannot be skimmed past. */
.ds-gate-row--gaps .ds-gate-k { color: var(--st-waiting-ink); background: var(--st-waiting-bg); }
.ds-gate-row--gaps .ds-gate-v { background: var(--st-waiting-bg); }

.ds-gate-line { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.ds-gate-line + .ds-gate-line { margin-top: var(--sp-1); }
.ds-gate-repo { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink); }
.ds-gate-num  { font-variant-numeric: tabular-nums; font-size: var(--fs-meta); }
.ds-gate-add  { color: var(--diff-add-ink); }
.ds-gate-del  { color: var(--diff-del-ink); }

.ds-gate-foot {
    display: flex; align-items: center; gap: var(--sp-2);
    flex-wrap: wrap;
    padding: var(--sp-3) var(--sp-4);
    background: var(--panel-bg);
    border-top: 1px solid var(--line);
}
.ds-gate-foot .ds-gate-undo { margin-left: auto; font-size: var(--fs-meta); color: var(--mute); }

/* The reasoning, folded. Available, never first. */
.ds-gate-why { border-top: 1px solid var(--line); }
.ds-gate-why > summary {
    padding: var(--sp-2) var(--sp-4);
    font-size: var(--fs-meta);
    color: var(--mute);
    cursor: pointer;
    list-style: none;
}
.ds-gate-why > summary::-webkit-details-marker { display: none; }
.ds-gate-why > summary::before { content: "▸ "; }
.ds-gate-why[open] > summary::before { content: "▾ "; }
.ds-gate-why > summary:hover { color: var(--ink); }
.ds-gate-why-body { padding: 0 var(--sp-4) var(--sp-4); font-size: var(--fs-dense); color: var(--soft); max-width: var(--ds-prose-max); }

/* =========================== 5. Empty / error / stale ============= */
.ds-empty {
    display: flex; flex-direction: column; align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-12) var(--sp-5);
    text-align: center;
    border: 1px dashed var(--line-firm);
    border-radius: var(--radius-md);
    background: var(--panel-bg);
}
.ds-empty--inline { padding: var(--sp-6) var(--sp-4); }
.ds-empty-icon { font-size: 22px; color: var(--mute); }
.ds-empty-title { font-size: var(--fs-lead); font-weight: var(--fw-semibold); }
.ds-empty-body { font-size: var(--fs-dense); color: var(--soft); max-width: 46ch; }

/* The distinction P5 exists for. "Nothing happened" and "we could not measure
   this" are different facts and must not render alike. */
.ds-unknown {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    font-size: var(--fs-dense);
    color: var(--st-unknown-ink);
    font-style: italic;
}
.ds-unknown::before { content: "—"; font-style: normal; }

.ds-stale-mark {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    font-size: var(--fs-micro);
    color: var(--st-stale-ink);
}

/* =========================== 6. Progress ========================== */
.ds-bar { height: 4px; background: var(--sunken); border-radius: var(--radius-full); overflow: hidden; }
.ds-bar-fill { height: 100%; background: var(--accent-fill); border-radius: var(--radius-full); transition: width var(--dur-slow) var(--ease-out); }
.ds-bar-fill--pass { background: var(--pass); }
.ds-bar-fill--warn { background: var(--st-waiting-ink); }
.ds-bar-fill--fail { background: var(--fail); }

/* Indeterminate, for work with no measurable fraction. Paired with a REASON in
   the markup -- Phase 2 §8 item 1: a spinner that stands in for a reason is the
   thing being removed, not the thing being styled. */
.ds-bar--indeterminate .ds-bar-fill {
    width: 35%;
    animation: ds-slide 1.4s var(--ease-in-out) infinite;
}
@keyframes ds-slide {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(300%); }
}

/* Budget meter: spend against a ceiling, with the ceiling visible. */
.ds-budget { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 120px; }
.ds-budget-nums { display: flex; justify-content: space-between; font-size: var(--fs-micro); font-variant-numeric: tabular-nums; color: var(--mute); }
.ds-budget--over .ds-bar-fill { background: var(--fail); }

/* Skeleton, for content whose SHAPE is known before its value. */
.ds-skel {
    background: linear-gradient(90deg, var(--sunken) 25%, var(--panel-bg) 50%, var(--sunken) 75%);
    background-size: 200% 100%;
    border-radius: var(--radius-xs);
    animation: ds-shimmer 1.3s linear infinite;
    color: transparent;
}
@keyframes ds-shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
    .ds-skel, .ds-bar--indeterminate .ds-bar-fill { animation: none; }
}

/* =========================== 7. Toast ============================ */
.ds-toasts {
    position: fixed; right: var(--sp-4); bottom: var(--sp-4);
    display: flex; flex-direction: column; gap: var(--sp-2);
    z-index: var(--z-toast);
    max-width: 380px;
}
.ds-toast {
    display: flex; align-items: flex-start; gap: var(--sp-2);
    padding: var(--sp-3);
    border: 1px solid var(--line-firm);
    border-radius: var(--radius-md);
    background: var(--raised);
    box-shadow: var(--shadow-lg);
    font-size: var(--fs-dense);
}
.ds-toast--pass { border-left: 3px solid var(--pass); }
.ds-toast--fail { border-left: 3px solid var(--fail); }

/* A chip reduced to its dot: the state, with the label carried by nearby text or
   by a visually-hidden span. Used on tabs and in dense lists where repeating the
   word would be noise. */
.ds-chip--bare {
    padding: 2px;
    border-radius: var(--radius-full);
    background: none;
    border-color: transparent;
}

/* Card edge for the remaining states, so a card can carry any of the eight. */
.ds-card--passed  { border-left: 3px solid var(--pass); }
.ds-card--pending { border-left: 3px solid var(--line-firm); }
.ds-card--stale   { border-left: 3px solid var(--st-stale-ink); }
.ds-card--unknown { border-left: 3px dashed var(--st-unknown-line); }

/* ======================================================================
   10. Analysis verdicts — a third vocabulary, three chips.
       See projects/templatetags/ds.py: a verdict is not a state and not a
       risk tier, and the three answers are not ordered. The dashed one is
       the point: "cannot tell" must look like neither answer, because a
       repository quietly folded into "no change" is one nobody looks at
       again until production.
   ====================================================================== */
.ds-chip--affected { color: var(--st-waiting-ink); background: var(--st-waiting-bg); border-color: var(--st-waiting-line); }
.ds-chip--none     { color: var(--mute); background: var(--sunken); border-color: var(--line); }
.ds-chip--none .ds-chip-dot { background: var(--mute); }
.ds-chip--affected .ds-chip-dot { background: var(--st-waiting-ink); }

/* ======================================================================
   11. The gate's scope list — added when the approve dialog became the
       card. Each line is a repository somebody is about to spend money
       on, so the line says what will happen to it and when.
   ====================================================================== */
.ds-gate-list { list-style: none; margin: 0 0 var(--sp-2); padding: 0; }
.ds-gate-pick {
    display: flex; align-items: center; gap: var(--sp-2);
    padding: var(--sp-1) 0;
    font-size: var(--fs-dense);
}
.ds-gate-pick.is-dragging { opacity: 0.4; }
.ds-gate-grip { color: var(--mute); cursor: grab; flex: none; }
.ds-gate-pick > code { font-size: var(--fs-meta); }
/* Right-aligned, so "starts now" and "queued · 6" line up down the list and the
   boundary at MAX_PARALLEL can be seen rather than counted. */
.ds-gate-when { margin-left: auto; flex: none; font-variant-numeric: tabular-nums; }
.ds-gate-pick.is-skipped > code { text-decoration: line-through; color: var(--mute); }

/* A caveat under a card whose body is flush to its edges. */
.ds-card-foot { padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line); margin: 0; }

/* A document, as a card you open. Two per repository: what the analysis proposed and
   what a person decided, which are different claims and never one overwritten file. */
/* A DOCUMENT, SHAPED LIKE ONE.
 * It stretched the full width of the report and pushed `Open` to the far edge, so the
 * title was at one side of a 900px row and the only word that looked like a control
 * was at the other, with white between them -- "Open is at the far right, and the name
 * is at the far left. Doesn't make sense." The whole card has always been the button;
 * nothing said so, because it was the same colour as the card it sat in.
 * Capped so the two ends stay in one glance, and given its own surface so it reads as
 * an object rather than as a line of text with a link after it. */
.ds-doccard {
    display: flex; align-items: center; gap: var(--sp-3);
    width: 100%; max-width: 32rem;
    padding: var(--sp-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--panel-bg);
    text-align: left;
    transition: border-color var(--dur-fast) var(--ease-out),
                background var(--dur-fast) var(--ease-out);
}
.ds-doccard:hover { background: var(--bg-hover); border-color: var(--line-firm); }
.ds-doccard > span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ds-doccard-title { font-size: var(--fs-dense); font-weight: var(--fw-medium); }
.ds-doccard > .ds-link { margin-left: auto; flex: none; }
.ds-doccard.is-reviewer { border-left: 2px solid var(--accent-text); }
.ds-fab[hidden] { display: none !important; }

/* ---------------------------------------------------------------------------
   THE CEILING, AT THE APPROVAL.

   Stated rather than chosen. The four policy layers can each only tighten, so there is
   nothing here for the approver to pick that the executor would honour -- what they need
   is to be told, before they press, how much the thing they are approving will do
   without asking again. See `_gate_ceiling` in work_views.
   --------------------------------------------------------------------------- */
.ds-ceiling { display: inline-flex; align-items: baseline; gap: var(--sp-1); }
.ds-ceiling > code {
    font-family: var(--font-mono);
    font-size: var(--fs-meta);
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    background: var(--panel-bg);
    border: 1px solid var(--line);
}
/* `full` is the one worth a colour. `read_only` and `standard` are the normal cases and
   tinting them teaches the reader to ignore the tint; a project running unattended is
   the state somebody should notice they are approving into. */
.ds-ceiling--full > code {
    background: var(--st-waiting-bg);
    border-color: var(--st-waiting-line);
    color: var(--st-waiting-ink);
}
/* An unreadable policy is held to the strictest level, and says so in the strongest
   colour the gate has: it is a configuration fault, not a setting. */
.ds-ceiling--unreadable > code {
    background: var(--st-failed-bg);
    border-color: var(--st-failed-line);
    color: var(--st-failed-ink);
}
.ds-gate-say { margin: var(--sp-1) 0 0; color: var(--mute); font-size: var(--fs-meta); }

/* Rows the gate will not open without an answer. One line per repository: what it is,
   why it is being asked about, and the control that answers it -- a list of names
   somebody has to go and find in a table of twelve is a list they tick past. */
.ds-gate-unruled li {
    display: flex; align-items: baseline; gap: var(--sp-2);
    flex-wrap: wrap; padding-block: 2px;
}
.ds-gate-unruled code { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--ink); }
.ds-gate-unruled .ds-meta { flex: 1 1 auto; min-width: 0; }
.ds-gate-unruled .ds-btn { flex: none; }

/* The critic's questions. A list of questions, so the repository is the label and the
   question is the content -- not the other way round, which would read as a list of
   repositories with findings attached and invite skimming the names. */
.ds-gate-asks li { display: flex; gap: var(--sp-2); align-items: baseline; padding-block: 2px; }
.ds-gate-asks code {
    font-family: var(--font-mono); font-size: var(--fs-meta);
    color: var(--ink); flex: none;
}
.ds-gate-asks > li > span { min-width: 0; }

/* ---------------------------------------------------------------------------
   CARD INTERIORS AND NOTES INSIDE THEM.

   `.ds-card-head` has existed since Phase 3; a card's BODY had no rule, so every
   screen that needed one invented its own padding. These five are the primitives the
   stage screens kept reaching for.
   --------------------------------------------------------------------------- */
.ds-card-body { padding: var(--sp-4); }
.ds-card-foot {
    padding: var(--sp-3) var(--sp-4);
    border-top: 1px solid var(--line);
    background: var(--panel-bg);
}

/* A note INSIDE a card already has the card's border around it, so it drops its own
   radius and runs to the card's edges -- a rounded box inside a rounded box reads as a
   second object rather than as part of this one. */
.ds-note--flush { border-radius: 0; border-left: 0; border-right: 0; margin: 0; }
.ds-card > .ds-note--flush:first-child { border-top: 0; }

/* A field and the control that commits it, on one line. The input takes the slack and
   the button never shrinks, because a Record button that wraps under a URL field looks
   like it belongs to whatever is below it. */
.ds-field-row { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.ds-field-row > .ds-input { flex: 1 1 18rem; min-width: 0; }
.ds-field-row > .ds-btn { flex: none; }

/* Something that is up and reachable, said with a state dot rather than a green word.
   The link is the point, so it is what carries the weight. */
.ds-live { display: flex; align-items: center; gap: var(--sp-2); margin: 0; }
.ds-live code { font-size: var(--fs-dense); }

/* The facts strip, reused inside a card where the header's own padding is already
   there. */
.ds-head-facts--flush { padding: 0; border: 0; margin: 0; }
/* A row of LABELLED controls, not of bare ones. `ds-field` is a column -- label above
   input -- so putting several side by side needs the children to grow, which the
   `> .ds-input` rule above cannot do because the input is a grandchild. Without this a
   three-field row collapses to three columns the width of their own text. */
.ds-field-row > .ds-field { flex: 1 1 12rem; min-width: 0; }
.ds-field-row > .ds-check { flex: none; align-self: flex-end; padding-bottom: var(--sp-2); }
