/*
 * ds/controls.css — buttons, inputs, form controls, segmented controls.
 * Everything a person clicks or types into.
 */

/* =========================== 1. Buttons =========================== */
.ds-btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--line-firm);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font-size: var(--fs-dense);
    font-weight: var(--fw-medium);
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
.ds-btn:hover { background: var(--sunken); text-decoration: none; }
.ds-btn:active { transform: translateY(0.5px); }

.ds-btn--primary {
    /* --accent-fill, not --accent: a filled button carries white text, and in
       dark mode --accent is tuned to be READ on a dark surface, which makes it
       too light to sit behind white (3.85:1). See theme-variables.css. */
    background: var(--accent-fill);
    border-color: var(--accent-fill);
    color: var(--on-accent);
}
.ds-btn--primary:hover { background: var(--accent-fill-hover); border-color: var(--accent-fill-hover); }

/* Danger is an OUTLINE, not a filled red block. A filled destructive button is
   the same visual weight as the primary action, so the eye lands on it first --
   and these gates are the ones where a misclick is expensive. */
.ds-btn--danger { color: var(--fail); border-color: var(--st-failed-line); background: var(--surface); }
.ds-btn--danger:hover { background: var(--st-failed-bg); border-color: var(--fail); }

.ds-btn--subtle { border-color: transparent; background: transparent; color: var(--soft); }
.ds-btn--subtle:hover { background: var(--sunken); color: var(--ink); }

.ds-btn--sm { padding: var(--sp-1) var(--sp-2); font-size: var(--fs-meta); }
.ds-btn--lg { padding: var(--sp-3) var(--sp-5); font-size: var(--fs-body); }
.ds-btn--block { width: 100%; }

.ds-btn:disabled,
.ds-btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.ds-btn:disabled:hover { background: var(--surface); }

/* Busy state. The label stays and a spinner joins it: replacing the label with a
   spinner removes the only text saying what is happening. */
.ds-btn[data-busy="true"] { pointer-events: none; opacity: 0.75; }
.ds-btn[data-busy="true"]::before {
    content: ""; width: 11px; height: 11px; flex: none;
    border: 2px solid currentColor; border-right-color: transparent;
    border-radius: var(--radius-full);
    animation: ds-spin 600ms linear infinite;
}
@keyframes ds-spin { to { transform: rotate(360deg); } }

.ds-iconbtn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; flex: none;
    border-radius: var(--radius-sm);
    color: var(--mute);
    transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.ds-iconbtn:hover { background: var(--sunken); color: var(--ink); }
/* Destructive, and only on hover. A delete control that is red at rest draws the eye to
   the one button on the row nobody should press by accident. */
.ds-iconbtn--danger:hover { background: var(--st-failed-bg); color: var(--fail); }

/* The "why" affordance. Not a tooltip: a tooltip cannot be read on a touch
   device and cannot be copied. */
.ds-btn-why {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    font-size: var(--fs-meta);
    color: var(--mute);
}
.ds-btn-why:hover { color: var(--accent-text); text-decoration: underline; }

/* =========================== 2. Inputs ============================ */
.ds-field { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.ds-field-label { font-size: var(--fs-meta); font-weight: var(--fw-medium); color: var(--soft); }
.ds-field-hint  { font-size: var(--fs-meta); color: var(--mute); }
/* WHY A STACK CONTROL IS EMPTY -- a reading we have no stack for, not a failed
   reading. Carries the waiting colour because it is a gap in the product, and muted
   grey would file it with the ordinary hints nobody reads. */
.ds-stack-why   { margin: var(--sp-1) 0 0; color: var(--st-waiting-ink); max-width: 34ch; }
.ds-field-error { font-size: var(--fs-meta); color: var(--fail); }

.ds-input, .ds-select, .ds-textarea {
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--line-firm);
    border-radius: var(--radius-sm);
    background: var(--bg-input);
    color: var(--ink);
    font-size: var(--fs-dense);
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
.ds-input::placeholder, .ds-textarea::placeholder { color: var(--mute); }
.ds-input:focus, .ds-select:focus, .ds-textarea:focus {
    outline: none;
    border-color: var(--accent-text);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.ds-input:disabled, .ds-select:disabled, .ds-textarea:disabled {
    background: var(--sunken); color: var(--mute); cursor: not-allowed;
}
.ds-input[aria-invalid="true"] { border-color: var(--fail); }
.ds-textarea { min-height: 84px; resize: vertical; line-height: var(--lh-normal); }
.ds-textarea--code { font-family: var(--font-mono); font-size: var(--fs-meta); }

.ds-select {
    appearance: none;
    padding-right: var(--sp-8);
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: calc(100% - 16px) 52%, calc(100% - 11px) 52%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}

/* Search. The icon is decorative; the input keeps its own accessible name. */
.ds-search { position: relative; display: flex; align-items: center; min-width: 0; }
.ds-search-icon {
    position: absolute; left: var(--sp-3);
    color: var(--mute); font-size: var(--fs-meta); pointer-events: none;
}
.ds-search .ds-input { padding-left: var(--sp-8); }

.ds-check { display: inline-flex; align-items: flex-start; gap: var(--sp-2); cursor: pointer; font-size: var(--fs-dense); }
.ds-check input { margin: 2px 0 0; accent-color: var(--accent-text); flex: none; }
.ds-check-text { min-width: 0; }
/* A checkbox Django rendered on its own, inside `ds-field` rather than inside a
   `ds-check` label. It gets the accent and nothing else: the label above it is the
   field's label, so wrapping it in the inline-flex row would indent it from its own
   heading. */
.ds-check-box { accent-color: var(--accent-text); width: 16px; height: 16px; }

/* Switch — for a setting that takes effect immediately. A checkbox is for a
   value you later submit; conflating them is why people press Save and nothing
   happens, or do not press Save and something does. */
.ds-switch-ctl {
    position: relative; display: inline-flex; align-items: center;
    width: 32px; height: 18px; flex: none;
    background: var(--line-firm);
    border-radius: var(--radius-full);
    transition: background var(--dur-base) var(--ease-out);
    cursor: pointer;
}
.ds-switch-ctl::after {
    content: ""; position: absolute; left: 2px;
    width: 14px; height: 14px; border-radius: var(--radius-full);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    transition: transform var(--dur-base) var(--ease-out);
}
.ds-switch-ctl[aria-checked="true"] { background: var(--accent-fill); }
.ds-switch-ctl[aria-checked="true"]::after { transform: translateX(14px); }

/* =========================== 3. Segmented ========================= */
.ds-seg {
    display: inline-flex; align-items: center;
    background: var(--sunken);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 2px;
    gap: 2px;
}
.ds-seg > button, .ds-seg > a {
    padding: var(--sp-1) var(--sp-3);
    border-radius: var(--radius-xs);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    color: var(--mute);
    white-space: nowrap;
    transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.ds-seg > button:hover, .ds-seg > a:hover { color: var(--ink); text-decoration: none; }
.ds-seg > [aria-pressed="true"], .ds-seg > [aria-selected="true"] {
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow-sm);
}

/* =========================== 4. Toolbar ========================== */
.ds-toolbar {
    display: flex; align-items: center; gap: var(--sp-3);
    flex-wrap: wrap;
    padding: var(--sp-3) 0;
    min-width: 0;
}
.ds-toolbar .ds-search { flex: 1 1 220px; max-width: 360px; }
/* A SELECT IN A TOOLBAR SIZES TO ITS CONTENT. `.ds-select` is `width: 100%` so that a
   select inside a `ds-field` fills its column, which is right there and wrong here: in
   a row of filters it made every one of them a full-width band on its own line, so four
   filters took four rows. Seen on the ticket list at 1440. */
.ds-toolbar > .ds-select,
.ds-toolbar > label > .ds-select { width: auto; flex: 0 1 auto; min-width: 8rem; max-width: 16rem; }
.ds-toolbar-count { font-size: var(--fs-meta); color: var(--mute); font-variant-numeric: tabular-nums; }
/* Pushes everything after it to the far end. A secondary control at the left edge
   beside the thing it is not about reads as the page's main action. */
.ds-toolbar-end { margin-left: auto; }

/* =========================== 5. Keys ============================= */
kbd.ds-kbd {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; padding: 1px var(--sp-1);
    border: 1px solid var(--line-firm);
    border-bottom-width: 2px;
    border-radius: var(--radius-xs);
    background: var(--surface);
    font-family: var(--font-ui);
    font-size: var(--fs-micro);
    color: var(--soft);
    white-space: nowrap;
}

/* ======================================================================
   8. Added for the Analysis rebuild.
   ====================================================================== */

/* A button that reads as a link. The product had three of these -- `.impact-note-more`,
   `.impact-stages-open`, `.review-doc-card-open` -- each defined once, in a different
   stylesheet, at a different size. */
.ds-link {
    font-size: inherit;
    color: var(--accent-text);
    text-decoration: underline;
    text-underline-offset: 2px;
    text-align: left;
}
.ds-link:hover { color: var(--accent-fill-hover); }

.ds-select--sm, .ds-input--sm {
    padding: var(--sp-1) var(--sp-2);
    font-size: var(--fs-meta);
    height: auto;
}

/* THE DOT MEANS THIS CANNOT BE UNDONE, and it goes on the control that actually does
   it. The old screen carried it on a button that opened a dialog -- warning about an
   action two presses away, and leaving the press that started the builds unmarked. */
.ds-btn--irreversible::after {
    content: "";
    width: 5px; height: 5px;
    border-radius: var(--radius-full);
    background: currentColor;
    opacity: 0.7;
    flex: none;
}

/* A control that reads as part of a sentence. Used where a note offers the action it is
   talking about -- "Not started: a, b. Run them" -- and a button with a border there
   would be a second object in a line of prose. */
.ds-linkbtn {
    background: none; border: 0; padding: 0;
    font: inherit; color: var(--accent-text); cursor: pointer;
    text-decoration: underline; text-underline-offset: 2px;
}
.ds-linkbtn:hover { text-decoration-thickness: 2px; }
.ds-linkbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

/* AN ACTION SETUP HAS SWITCHED OFF, and the sentence saying which step switched it off.
   `ds-btn--off` is for an ANCHOR: a link cannot be disabled, and one styled to look
   dead that still navigates is worse than either state. The reason always travels with
   it -- a greyed control with no explanation is the fault this replaced. */
.ds-btn--off {
    opacity: .55; cursor: not-allowed; pointer-events: none;
}
.ds-blocked-say { color: var(--st-waiting-ink); }

/* A QUESTION WITH SOMEWHERE TO PUT THE ANSWER.
   The approve dialog listed the agent's open questions and offered a checkbox. For a
   developer that is the wrong verb -- they are being asked something they usually
   know -- so each question now carries its own box. One per question, never one per
   repository: three answers in a single field is three answers nobody can tell apart
   afterwards, and the record is the point of asking. */
.ds-qa-item { display: grid; gap: var(--sp-1); margin-bottom: var(--sp-2); }
.ds-qa-q { font-size: var(--fs-meta); }
.ds-qa-a { width: 100%; max-width: 46rem; resize: vertical; }
/* The decision control writes on change; this is the only thing that says so before
   it happens. Quiet, because it is a reassurance rather than an instruction. */
.ds-decide-hint { color: var(--mute); }
