/* ==========================================================================
   components.css — the component library

   Convention: every component that has variants declares its own private
   custom properties (`--btn-bg`, `--chip-fg`, …) in the block rule, and a
   variant only reassigns those. Adding a variant therefore never means
   restating layout, and never means a new selector deeper than one class.

   State comes from ARIA and native attributes wherever one exists —
   `[disabled]`, `[aria-busy]`, `[aria-pressed]`, `[aria-selected]`,
   `[aria-current]`, `[aria-invalid]`, `[aria-sort]`, `:checked`. A state
   class is a last resort, because a state class can drift out of sync with
   what a screen reader is told; an attribute cannot.

   Motion: 120–260ms, decelerating. Nothing overshoots or bounces.
   ========================================================================== */

@layer components {
  /* ======================================================================
     Motion primitives
     ====================================================================== */
  @keyframes agp-spin {
    to { transform: rotate(1turn); }
  }

  @keyframes agp-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
  }

  @keyframes agp-rise-in {
    from { opacity: 0; transform: translateY(var(--space-2)); }
    to { opacity: 1; transform: none; }
  }

  @keyframes agp-rise-out {
    from { opacity: 1; transform: none; }
    to { opacity: 0; transform: translateY(var(--space-1)); }
  }

  @keyframes agp-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.45; }
  }

  /* ======================================================================
     Buttons
     ====================================================================== */
  .btn {
    --btn-h: var(--control-h-md);
    --btn-px: var(--space-3);
    --btn-fs: var(--text-base);
    --btn-bg: var(--bg-surface);
    --btn-fg: var(--fg-primary);
    --btn-border: var(--border-default);
    --btn-bg-hover: var(--bg-hover);
    --btn-border-hover: var(--border-strong);
    --btn-bg-active: var(--bg-active);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    block-size: var(--btn-h);
    padding-inline: var(--btn-px);
    background-color: var(--btn-bg);
    color: var(--btn-fg);
    border: 1px solid var(--btn-border);
    border-radius: var(--radius-control);
    font-family: inherit;
    font-size: var(--btn-fs);
    line-height: 1;
    font-weight: var(--weight-medium);
    white-space: nowrap;
    text-decoration: none;
    user-select: none;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
  }

  .btn:hover:not(:disabled):not([aria-disabled="true"]):not([aria-busy="true"]) {
    background-color: var(--btn-bg-hover);
    border-color: var(--btn-border-hover);
  }

  .btn:active:not(:disabled):not([aria-disabled="true"]):not([aria-busy="true"]) {
    background-color: var(--btn-bg-active);
  }

  .btn:disabled,
  .btn[aria-disabled="true"] {
    opacity: 0.45;
    pointer-events: none;
  }

  .btn--primary {
    --btn-bg: var(--accent);
    --btn-fg: var(--accent-fg);
    --btn-border: var(--accent);
    --btn-bg-hover: var(--accent-hover);
    --btn-border-hover: var(--accent-hover);
    --btn-bg-active: var(--accent-pressed);
  }

  .btn--secondary {
    --btn-bg: var(--bg-surface);
    --btn-fg: var(--fg-primary);
    --btn-border: var(--border-default);
  }

  .btn--ghost {
    --btn-bg: transparent;
    --btn-fg: var(--fg-secondary);
    --btn-border: transparent;
    --btn-bg-hover: var(--bg-hover);
    --btn-border-hover: transparent;
    --btn-bg-active: var(--bg-active);
  }

  .btn--ghost:hover:not(:disabled) {
    color: var(--fg-primary);
  }

  .btn--danger {
    --btn-bg: var(--danger);
    --btn-fg: var(--danger-fg);
    --btn-border: var(--danger);
    --btn-bg-hover: color-mix(in srgb, var(--danger) 86%, var(--fg-primary));
    --btn-border-hover: var(--btn-bg-hover);
    --btn-bg-active: color-mix(in srgb, var(--danger) 74%, var(--fg-primary));
  }

  /* A destructive action that is not the primary action on the surface. */
  .btn--danger-quiet {
    --btn-bg: transparent;
    --btn-fg: var(--danger-text);
    --btn-border: var(--border-default);
    --btn-bg-hover: var(--danger-subtle);
    --btn-border-hover: var(--danger-border);
    --btn-bg-active: var(--danger-subtle);
  }

  .btn--sm {
    --btn-h: var(--control-h-sm);
    --btn-px: var(--space-2);
    --btn-fs: var(--text-sm);
    gap: var(--space-1);
  }

  .btn--lg {
    --btn-h: var(--control-h-lg);
    --btn-px: var(--space-4);
    --btn-fs: var(--text-md);
  }

  .btn--icon {
    inline-size: var(--btn-h);
    padding-inline: 0;
    gap: 0;
  }

  /* `full=True` on components/button.html. One name for the idea, and it is
     this one: the sign-in button is the only caller and it wrote `btn--full`
     at a rule that never existed. */
  .btn--block {
    inline-size: 100%;
  }

  /* Loading. The label is hidden rather than replaced so the button keeps
     its width and the row does not reflow mid-click. */
  .btn[aria-busy="true"] {
    position: relative;
    color: transparent;
    pointer-events: none;
  }

  .btn[aria-busy="true"]::after {
    content: "";
    position: absolute;
    inline-size: var(--icon-md);
    block-size: var(--icon-md);
    border: 2px solid var(--btn-fg);
    border-block-start-color: transparent;
    border-radius: var(--radius-pill);
    animation: agp-spin 620ms linear infinite;
  }

  .btn--sm[aria-busy="true"]::after {
    inline-size: var(--icon-sm);
    block-size: var(--icon-sm);
  }

  @media (prefers-reduced-motion: reduce) {
    .btn[aria-busy="true"]::after {
      animation: none;
      border-block-start-color: var(--btn-fg);
      opacity: 0.4;
    }
  }

  /* Pressed. `aria-pressed` is set by JS on a `.btn` in exactly one place —
     Settings > Voice's microphone Test button, which becomes Stop while the
     meter is running — and no sheet in the product painted it, so the one
     control that reports whether your microphone is live looked identical
     either way. The word changes; now the button does too. `.segmented__item`
     and the composer's tools each paint their own pressed state already; this
     is the plain `.btn`'s, drawn from its own variant properties so it works
     on every variant rather than only the one caller. */
  .btn[aria-pressed="true"] {
    --btn-bg: var(--bg-selected);
    --btn-fg: var(--accent-text);
    --btn-border: var(--accent-border);
    --btn-bg-hover: var(--bg-selected);
    --btn-border-hover: var(--accent);
  }

  .btn__count {
    font-variant-numeric: tabular-nums;
    color: var(--fg-muted);
  }

  /* ======================================================================
     Fields
     ====================================================================== */
  .field {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-inline-size: 0;
  }

  .field__label {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    font-weight: var(--weight-medium);
    color: var(--fg-secondary);
  }

  .field__label[data-required]::after {
    content: "*";
    color: var(--danger);
  }

  .field__hint {
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    color: var(--fg-muted);
  }

  .field__error {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    color: var(--danger-text);
  }

  .field--inline {
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
  }

  .input,
  .textarea,
  .select {
    inline-size: 100%;
    min-inline-size: 0;
    background-color: var(--bg-surface);
    color: var(--fg-primary);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-control);
    font-family: inherit;
    font-size: var(--text-base);
    line-height: var(--lh-base);
    transition: border-color var(--dur-fast) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out);
  }

  .input,
  .select {
    block-size: var(--control-h-md);
    padding-inline: var(--space-3);
  }

  .textarea {
    padding: var(--space-2) var(--space-3);
    min-block-size: calc(var(--control-h-md) * 2);
  }

  .input:hover:not(:disabled):not(:read-only),
  .textarea:hover:not(:disabled):not(:read-only),
  .select:hover:not(:disabled) {
    border-color: var(--border-strong);
  }

  .input:focus,
  .textarea:focus,
  .select:focus {
    border-color: var(--accent);
  }

  .input:disabled,
  .textarea:disabled,
  .select:disabled {
    background-color: var(--bg-sunken);
    color: var(--fg-faint);
    border-color: var(--border-subtle);
  }

  .input:read-only,
  .textarea:read-only {
    background-color: var(--bg-sunken);
    color: var(--fg-secondary);
  }

  .input[aria-invalid="true"],
  .textarea[aria-invalid="true"],
  .select[aria-invalid="true"] {
    border-color: var(--danger);
  }

  .input--sm { block-size: var(--control-h-sm); font-size: var(--text-sm); padding-inline: var(--space-2); }
  .input--lg { block-size: var(--control-h-lg); font-size: var(--text-md); }
  .input--mono { font-family: var(--font-mono); font-size: var(--text-sm); }
  .input--numeric { text-align: end; font-variant-numeric: tabular-nums; }

  .select {
    appearance: none;
    padding-inline-end: var(--space-8);
    background-image: var(--icon-chevron);
    background-repeat: no-repeat;
    background-position: right var(--space-2) center;
    background-size: var(--icon-md) var(--icon-md);
    cursor: pointer;
  }

  [dir="rtl"] .select {
    background-position: left var(--space-2) center;
    padding-inline-end: var(--space-8);
  }

  .input-group {
    position: relative;
    display: flex;
    align-items: center;
    min-inline-size: 0;
  }

  .input-group__icon {
    position: absolute;
    inset-inline-start: var(--space-3);
    display: grid;
    place-items: center;
    color: var(--fg-faint);
    pointer-events: none;
  }

  .input-group__icon--end {
    inset-inline-start: auto;
    inset-inline-end: var(--space-3);
  }

  .input-group > .input-group__icon ~ .input,
  .input-group--search > .input {
    padding-inline-start: var(--space-8);
  }

  .input-group__affix {
    display: inline-flex;
    align-items: center;
    block-size: var(--control-h-md);
    padding-inline: var(--space-3);
    background-color: var(--bg-sunken);
    border: 1px solid var(--border-default);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    white-space: nowrap;
  }

  .input-group__affix:first-child {
    border-inline-end: 0;
    border-start-start-radius: var(--radius-control);
    border-end-start-radius: var(--radius-control);
  }

  .input-group__affix:last-child {
    border-inline-start: 0;
    border-start-end-radius: var(--radius-control);
    border-end-end-radius: var(--radius-control);
  }

  .input-group__affix:first-child + .input {
    border-start-start-radius: 0;
    border-end-start-radius: 0;
  }

  .input-group > .input:has(+ .input-group__affix) {
    border-start-end-radius: 0;
    border-end-end-radius: 0;
  }

  /* ======================================================================
     Radio · switch
     ====================================================================== */
  /* The three `background-*` positioning declarations that used to be here
     placed `.checkbox`'s tick, which was a background image. A radio's dot is
     an inset shadow, so they went with it. */
  .radio {
    appearance: none;
    flex: 0 0 auto;
    inline-size: var(--icon-md);
    block-size: var(--icon-md);
    margin: 0;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
  }

  .radio:hover:not(:disabled) {
    border-color: var(--accent);
  }

  /* Ring in accent, dot in the accent's own foreground — one inset shadow,
     no pseudo-element, because a replaced input cannot be relied on for one. */
  .radio:checked {
    background-color: var(--accent-fg);
    border-color: var(--accent);
    box-shadow: inset 0 0 0 4px var(--accent);
  }

  .radio:active:not(:disabled) {
    background-color: var(--bg-active);
  }

  .radio:disabled {
    opacity: 0.45;
    cursor: not-allowed;
  }

  .switch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
  }

  /* The input is native and visible. The drawn `__track`/`__thumb` pair that
     used to hide it was written by no template and no module; the product's
     drawn switch is `.toggle`. */
  .switch__input {
    accent-color: var(--accent);
    cursor: pointer;
  }

  .switch:has(.switch__input:disabled) {
    cursor: not-allowed;
  }

  .switch__label {
    font-size: var(--text-base);
    line-height: var(--lh-base);
  }

  /* ======================================================================
     Chips
     ====================================================================== */
  .chip {
    --chip-bg: var(--bg-surface);
    --chip-fg: var(--fg-secondary);
    --chip-border: var(--border-default);

    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-block-size: var(--control-h-sm);
    max-inline-size: 100%;
    padding-inline: var(--space-3);
    background-color: var(--chip-bg);
    color: var(--chip-fg);
    border: 1px solid var(--chip-border);
    border-radius: var(--radius-pill);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
  }

  button.chip,
  a.chip {
    cursor: pointer;
  }

  button.chip:hover:not(:disabled),
  a.chip:hover {
    --chip-bg: var(--accent-subtle);
    --chip-border: var(--accent-border);
    --chip-fg: var(--accent-text);
  }

  button.chip:active:not(:disabled) {
    --chip-bg: var(--bg-active);
  }

  .chip[aria-pressed="true"],
  .chip[data-selected="true"] {
    --chip-bg: var(--accent-subtle);
    --chip-border: var(--accent-border);
    --chip-fg: var(--accent-text);
  }

  .chip:disabled {
    opacity: 0.45;
    pointer-events: none;
  }

  .chip--sm {
    min-block-size: var(--space-5);
    padding-inline: var(--space-2);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
  }

  .chip--removable {
    padding-inline-end: var(--space-1);
  }

  .chip__remove {
    display: grid;
    place-items: center;
    inline-size: var(--space-4);
    block-size: var(--space-4);
    border-radius: var(--radius-pill);
    color: var(--fg-muted);
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
  }

  .chip__remove::before {
    content: "";
    inline-size: var(--icon-xs);
    block-size: var(--icon-xs);
    background-color: currentColor;
    mask-image: var(--mask-close);
    mask-size: 100% 100%;
    -webkit-mask-image: var(--mask-close);
    -webkit-mask-size: 100% 100%;
  }

  .chip__remove:hover {
    background-color: var(--bg-active);
    color: var(--fg-primary);
  }

  .chip-set {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    min-inline-size: 0;
  }

  /* ======================================================================
     Badges · pills · status
     ====================================================================== */
  .badge {
    --badge-bg: var(--bg-sunken);
    --badge-fg: var(--fg-secondary);
    --badge-border: transparent;

    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-block-size: var(--space-5);
    padding-inline: var(--space-2);
    background-color: var(--badge-bg);
    color: var(--badge-fg);
    border: 1px solid var(--badge-border);
    border-radius: var(--radius-control);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
  }

  .badge--pill { border-radius: var(--radius-pill); }

  /* Size is height and inline padding, never type: `--text-xs` is the floor
     of the scale and a status word below 12px stops being read and starts
     being decoration. `.badge` itself IS `sm` — 74 of the product's 81 badge
     call sites ask for it and every bare `.badge` on the chat surface is one
     — so `--sm` reassigns nothing and carries no rule, the same convention
     `.btn--md` and `.badge--neutral` follow. The two that do differ:

       --xs  the environment chip in the sidebar head, beside the brand
       --md  a badge that stands on its own in a card header, not in a row */
  .badge--xs {
    min-block-size: var(--space-4);
    padding-inline: var(--space-1);
  }

  .badge--md {
    min-block-size: var(--space-6);
    padding-inline: var(--space-3);
  }

  .badge--accent  { --badge-bg: var(--accent-subtle);  --badge-fg: var(--accent-text);  --badge-border: var(--accent-border); }
  .badge--success { --badge-bg: var(--success-subtle); --badge-fg: var(--success-text); --badge-border: var(--success-border); }
  .badge--warning { --badge-bg: var(--warning-subtle); --badge-fg: var(--warning-text); --badge-border: var(--warning-border); }
  .badge--danger  { --badge-bg: var(--danger-subtle);  --badge-fg: var(--danger-text);  --badge-border: var(--danger-border); }
  .badge--info    { --badge-bg: var(--info-subtle);    --badge-fg: var(--info-text);    --badge-border: var(--info-border); }
  .badge--outline { --badge-bg: transparent;           --badge-fg: var(--fg-muted);     --badge-border: var(--border-default); }

  .badge--solid-accent  { --badge-bg: var(--accent);  --badge-fg: var(--accent-fg);  --badge-border: var(--accent); }
  .badge--solid-danger  { --badge-bg: var(--danger);  --badge-fg: var(--danger-fg);  --badge-border: var(--danger); }
  .badge--solid-warning { --badge-bg: var(--warning); --badge-fg: var(--warning-fg); --badge-border: var(--warning); }

  /* The badge is `nowrap` and its parts are flex items, so in a narrow cell
     something has to give. The word gives; the glyph never does. */
  .badge__icon {
    flex: 0 0 auto;
  }

  .badge__label {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .badge__dot {
    inline-size: var(--size-dot);
    block-size: var(--size-dot);
    border-radius: var(--radius-pill);
    background-color: currentColor;
    flex: 0 0 auto;
  }

  .badge__count {
    font-variant-numeric: tabular-nums;
  }

  /* Change-state vocabulary from the design diagram: what a component or a
     catalogue entry did between revisions. Always uppercase, so the four read
     as one family and never as prose. */
  .badge--unchanged,
  .badge--new,
  .badge--changed,
  .badge--promoted {
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
  }

  .badge--unchanged { --badge-bg: var(--bg-sunken);     --badge-fg: var(--fg-muted);     --badge-border: var(--border-default); }
  .badge--new       { --badge-bg: var(--accent-subtle); --badge-fg: var(--accent-text);  --badge-border: var(--accent-border); }
  .badge--changed   { --badge-bg: var(--warning-subtle);--badge-fg: var(--warning-text); --badge-border: var(--warning-border); }
  .badge--promoted  { --badge-bg: var(--info-subtle);   --badge-fg: var(--info-text);    --badge-border: var(--info-border); }

  /* ======================================================================
     Cards
     ====================================================================== */
  .card {
    display: flex;
    flex-direction: column;
    min-inline-size: 0;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
  }

  .card--raised { box-shadow: var(--shadow-xs); }

  .card__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4);
    min-inline-size: 0;
  }

  .card__header + .card__body {
    padding-block-start: 0;
  }

  .card__header--divided {
    border-block-end: 1px solid var(--border-subtle);
  }

  .card__header--divided + .card__body {
    padding-block-start: var(--space-4);
  }

  .card__title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-inline-size: 0;
    font-size: var(--text-md);
    line-height: var(--lh-md);
    font-weight: var(--weight-semibold);
    color: var(--fg-primary);
  }

  .card__meta {
    margin-block-start: var(--space-1);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
  }

  .card__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
  }

  .card__body {
    padding: var(--space-4);
    min-inline-size: 0;
    flex: 1 1 auto;
  }

  .card__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-block-start: 1px solid var(--border-subtle);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
  }

  .card--flush > .card__body { padding: 0; }

  .card--interactive {
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
  }

  .card--interactive:hover {
    border-color: var(--border-default);
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
  }

  .card--interactive:active {
    transform: none;
    box-shadow: none;
  }

  /* ======================================================================
     Tables
     ====================================================================== */
  .table-wrap {
    inline-size: 100%;
    max-inline-size: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
  }

  /* Sticky headers need the wrapper to own vertical scrolling too. Set the
     ceiling per surface: style="--table-max-h: 60vh". */
  .table-wrap--sticky {
    max-block-size: var(--table-max-h, 60vh);
    overflow-y: auto;
  }

  .table {
    inline-size: 100%;
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
  }

  /* The data table charts.js writes under every chart — the numbers behind the
     picture, hidden with `.sr-only` but real markup a reader can be shown.
     Until these selectors carried it, and the two cell rules below, it
     rendered in the BROWSER's table language: 14px headings with 1px of
     padding and 24px rows, beside 12px tracked capitals in `8px 12px` and
     45px rows everywhere else in the product — a second table language in the
     same page. One table, one treatment, whether it is drawn on /operations
     or under an answer.

     Type only, deliberately: `inline-size: 100%` is NOT set here, because
     `.sr-only` holds these tables to 1px and this rule would out-cascade it
     (same specificity, later layer) into a full-width absolutely-positioned
     box able to extend its container's scrollable area. The narrower fix is
     one word in charts.js — emit `class="table sr-only"`, which brings the
     whole recipe including the sizing it was written with — and that is f3's
     to make; this holds either way. */
  [data-chart-table],
  .artifact__data {
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
  }

  /* `.truncate` on a table cell needs a width the auto table algorithm will
     respect, or the column simply grows to fit and the corpus table ends up
     six thousand pixels wide inside its scroller. The cap is a reading width,
     not a layout guess; anything longer is reachable through the row itself. */
  .table td.truncate,
  .table th.truncate {
    max-inline-size: 36ch;
  }

  .table thead th,
  [data-chart-table] thead th,
  .artifact__data thead th {
    position: sticky;
    inset-block-start: 0;
    z-index: var(--z-sticky);
    padding: var(--space-2) var(--space-3);
    background-color: var(--bg-surface);
    color: var(--fg-muted);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    white-space: nowrap;
    /* A collapsed border does not travel with a sticky cell; a shadow does. */
    box-shadow: inset 0 -1px 0 var(--border-default);
  }

  .table tbody td,
  [data-chart-table] tbody td,
  [data-chart-table] tbody th,
  .artifact__data tbody td,
  .artifact__data tbody th {
    padding: var(--space-3);
    border-block-end: 1px solid var(--border-subtle);
    color: var(--fg-secondary);
    vertical-align: top;
  }

  .table tbody tr:last-child td {
    border-block-end: 0;
  }

  .table tbody tr {
    transition: background-color var(--dur-fast) var(--ease-out);
  }

  .table tbody tr:hover {
    background-color: var(--bg-hover);
  }

  .table tbody tr[aria-selected="true"] {
    background-color: var(--bg-selected);
  }

  .table tbody tr[data-status="failed"] td:first-child {
    box-shadow: inset 2px 0 0 var(--danger);
  }

  .table__primary {
    color: var(--fg-primary);
    font-weight: var(--weight-medium);
  }

  .table__num,
  .table [data-align="end"] {
    text-align: end;
    font-variant-numeric: tabular-nums;
  }

  .table__select {
    inline-size: var(--space-10);
    text-align: center;
  }

  .table__actions {
    inline-size: var(--space-12);
    text-align: end;
    white-space: nowrap;
  }

  .table--compact tbody td { padding: var(--space-2) var(--space-3); }
  .table--nowrap td,
  .table--nowrap th { white-space: nowrap; }
  .table--fixed { table-layout: fixed; }

  .table__sort {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    transition: color var(--dur-fast) var(--ease-out);
  }

  .table__sort::after {
    content: "";
    inline-size: var(--icon-sm);
    block-size: var(--icon-sm);
    background-color: currentColor;
    mask-image: var(--mask-sort);
    mask-size: 100% 100%;
    -webkit-mask-image: var(--mask-sort);
    -webkit-mask-size: 100% 100%;
    opacity: 0.35;
    transition: opacity var(--dur-fast) var(--ease-out);
  }

  .table__sort:hover { color: var(--fg-primary); }
  .table__sort:hover::after { opacity: 0.7; }

  th[aria-sort="ascending"] .table__sort,
  th[aria-sort="descending"] .table__sort {
    color: var(--fg-primary);
  }

  th[aria-sort="ascending"] .table__sort::after {
    mask-image: var(--mask-sort-asc);
    -webkit-mask-image: var(--mask-sort-asc);
    opacity: 1;
  }

  th[aria-sort="descending"] .table__sort::after {
    mask-image: var(--mask-sort-desc);
    -webkit-mask-image: var(--mask-sort-desc);
    opacity: 1;
  }

  .table__empty td {
    padding: var(--space-10) var(--space-4);
    text-align: center;
    color: var(--fg-muted);
  }

  /* ======================================================================
     Tabs
     ====================================================================== */
  .tabs {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    border-block-end: 1px solid var(--border-subtle);
    overflow-x: auto;
    scrollbar-width: none;
  }

  .tabs::-webkit-scrollbar { display: none; }

  .tab {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--control-h-lg);
    padding-inline: var(--space-3);
    color: var(--fg-muted);
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    border-start-start-radius: var(--radius-control);
    border-start-end-radius: var(--radius-control);
    transition: color var(--dur-fast) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out);
  }

  .tab:hover:not(:disabled) {
    color: var(--fg-primary);
    background-color: var(--bg-hover);
  }

  /* The selected state is `aria-current="page"` and nothing else. The bar
     used to state it twice — `aria-selected` for a tablist that had no panels
     to swap, `aria-current` for the fact that this tab IS the document — and
     components/tabs.html has since dropped the tab roles it could not honour.
     One attribute now carries both jobs: what the reader is told, and what
     this sheet paints from. */
  .tab[aria-current="page"] {
    color: var(--fg-primary);
  }

  .tab[aria-current="page"]::after {
    content: "";
    position: absolute;
    inset-inline: var(--space-2);
    inset-block-end: -1px;
    block-size: 2px;
    background-color: var(--accent);
    border-start-start-radius: var(--radius-pill);
    border-start-end-radius: var(--radius-pill);
  }

  .tab:disabled {
    opacity: 0.45;
    pointer-events: none;
  }

  /* The count is data, not decoration — "3" is how many approvals are waiting
     on a person. At `--fg-faint` it measured 2.60:1 on the canvas, so the one
     number on the bar that a reader is actually counting was the least legible
     thing on it. `--fg-muted` is the same graphite two steps up (5.54:1 light,
     5.96:1 dark), and on the selected tab it follows the tab's own ink. */
  .tab__count {
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--fg-muted);
  }

  .tab[aria-current="page"] .tab__count {
    color: var(--fg-secondary);
  }

  /* ======================================================================
     Segmented control
     ====================================================================== */
  .segmented {
    display: inline-flex;
    align-items: center;
    gap: var(--space-05);
    padding: var(--space-05);
    background-color: var(--bg-sunken);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-control);
  }

  /* `.segmented > button` carries the same styles as `.segmented__item`
     because the account-menu template ships bare buttons and app.js only adds
     the class after boot — without the child selector the control renders
     unstyled until JS runs, and never styles at all if JS fails. */
  .segmented > button,
  .segmented__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    min-block-size: var(--control-h-sm);
    padding-inline: var(--space-3);
    border-radius: var(--radius-control);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
  }

  .segmented > button:hover:not(:disabled),
  .segmented__item:hover:not(:disabled) {
    color: var(--fg-primary);
  }

  .segmented > button[aria-pressed="true"],
  .segmented__item[aria-pressed="true"],
  .segmented__item[aria-selected="true"],
  .segmented__item[aria-checked="true"] {
    background-color: var(--bg-surface);
    color: var(--fg-primary);
    box-shadow: var(--shadow-xs);
  }

  .segmented__item:disabled {
    opacity: 0.45;
    pointer-events: none;
  }

  /* ======================================================================
     Avatar
     ====================================================================== */
  .avatar {
    --avatar-color: var(--fg-muted);

    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    inline-size: var(--space-6);
    block-size: var(--space-6);
    overflow: hidden;
    border-radius: var(--radius-pill);
    background-color: var(--bg-inset);
    color: var(--fg-secondary);
    font-size: var(--text-xs);
    line-height: 1;
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    user-select: none;
  }

  /* identity_user.avatar_color is per-user data, so the only way to derive a
     legible pair from it is at paint time. This is the one place the system
     mixes a colour instead of naming one. */
  @supports (color: color-mix(in srgb, red, blue)) {
    .avatar {
      background-color: color-mix(in srgb, var(--avatar-color) 16%, var(--bg-surface));
      color: color-mix(in srgb, var(--avatar-color) 72%, var(--fg-primary));
      box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--avatar-color) 26%, transparent);
    }
  }

  .avatar > img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
  }

  .avatar--sm { inline-size: var(--space-5);  block-size: var(--space-5); }
  .avatar--md { inline-size: var(--space-8);  block-size: var(--space-8);  font-size: var(--text-sm); }
  .avatar--lg { inline-size: var(--space-10); block-size: var(--space-10); font-size: var(--text-base); }

  /* The eight identity colours. `identity.models.AVATAR_COLORS` names them,
     `pick_avatar_color` hashes an email into one, and the template writes
     `avatar--<name>`; all any of them does is hand `--avatar-color` to the
     mix above, which is why one token per name serves both themes. Without
     these rules the whole per-person colour system fell back to the block's
     `var(--fg-muted)` and every monogram in the product painted the same
     grey. Alphabetical, so a ninth name has an obvious home. */
  .avatar--amber { --avatar-color: var(--avatar-amber); }
  .avatar--blue  { --avatar-color: var(--avatar-blue); }
  .avatar--clay  { --avatar-color: var(--avatar-clay); }
  .avatar--moss  { --avatar-color: var(--avatar-moss); }
  .avatar--rust  { --avatar-color: var(--avatar-rust); }
  .avatar--sand  { --avatar-color: var(--avatar-sand); }
  .avatar--slate { --avatar-color: var(--avatar-slate); }
  .avatar--teal  { --avatar-color: var(--avatar-teal); }

  /* ======================================================================
     Kbd
     ====================================================================== */
  .kbd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--space-5);
    block-size: var(--space-5);
    padding-inline: var(--space-1);
    background-color: var(--bg-surface);
    color: var(--fg-muted);
    border: 1px solid var(--border-default);
    border-block-end-width: 2px;
    border-radius: var(--radius-control);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1;
  }

  .kbd-set {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--fg-faint);
    font-size: var(--text-xs);
  }

  /* ======================================================================
     Tooltip
     ====================================================================== */
  /* CSS-only tooltip for a value that is a convenience, never the only place
     information lives — the trace's true elapsed time, a truncated cell. */
  [data-tip] {
    position: relative;
  }

  [data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    inset-block-end: calc(100% + var(--space-1));
    inset-inline-start: 50%;
    translate: -50% 0;
    z-index: var(--z-tooltip);
    inline-size: max-content;
    max-inline-size: 24ch;
    padding: var(--space-1) var(--space-2);
    background-color: var(--fg-primary);
    color: var(--bg-surface);
    border-radius: var(--radius-control);
    box-shadow: var(--shadow-md);
    font-family: var(--font-sans);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    font-weight: var(--weight-regular);
    letter-spacing: normal;
    text-transform: none;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease-out),
                visibility var(--dur-fast) var(--ease-out);
  }

  [data-tip]:hover::after,
  [data-tip]:focus-visible::after {
    opacity: 1;
    visibility: visible;
  }

  /* ======================================================================
     Popover · menu
     ====================================================================== */
  .popover {
    z-index: var(--z-popover);
    min-inline-size: 13rem;
    max-inline-size: 22rem;
    padding: var(--space-1);
    /* The floating-panel recipe: raised ground, default border, card corners,
       lg shadow — shared with .modal, .dialog and the account menu, so every
       surface that floats reads as one product. */
    background-color: var(--bg-raised);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-lg);
    animation: agp-rise-in var(--dur-fast) var(--ease-out);
  }

  /* Native popover=... elements come with UA positioning that fights ours. */
  .popover:where([popover]) {
    inset: auto;
    margin: 0;
    overflow: visible;
  }

  .popover::backdrop {
    background: transparent;
  }

  .popover--wide { max-inline-size: 30rem; }
  .popover--pad { padding: var(--space-4); }

  .popover__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-2) var(--space-1);
    color: var(--fg-faint);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
  }

  .popover__footer {
    margin-block-start: var(--space-1);
    padding: var(--space-2);
    border-block-start: 1px solid var(--border-subtle);
  }

  .menu-item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    inline-size: 100%;
    min-block-size: var(--control-h-md);
    padding-inline: var(--space-2);
    border-radius: var(--radius-control);
    color: var(--fg-secondary);
    font-size: var(--text-base);
    line-height: var(--lh-base);
    text-align: start;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
  }

  .menu-item:hover:not(:disabled),
  .menu-item[data-active="true"] {
    background-color: var(--bg-hover);
    color: var(--fg-primary);
  }

  .menu-item:active:not(:disabled) {
    background-color: var(--bg-active);
  }

  .menu-item:disabled {
    opacity: 0.45;
    pointer-events: none;
  }

  .menu-item--danger { color: var(--danger-text); }

  .menu-item--danger:hover:not(:disabled) {
    background-color: var(--danger-subtle);
    color: var(--danger-text);
  }

  .menu-item__label {
    min-inline-size: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .menu-item__shortcut {
    margin-inline-start: auto;
    flex: 0 0 auto;
    color: var(--fg-faint);
    font-size: var(--text-xs);
    font-family: var(--font-mono);
  }

  .menu-item[aria-checked="true"]::after {
    content: "";
    margin-inline-start: auto;
    flex: 0 0 auto;
    inline-size: var(--icon-sm);
    block-size: var(--icon-sm);
    background-color: var(--accent);
    mask-image: var(--mask-check);
    mask-size: 100% 100%;
    -webkit-mask-image: var(--mask-check);
    -webkit-mask-size: 100% 100%;
  }

  /* ======================================================================
     Modal
     ====================================================================== */
  .modal {
    inline-size: min(100% - var(--space-8), 34rem);
    max-block-size: min(100% - var(--space-8), 46rem);
    margin: auto;
    background-color: var(--bg-raised);
    color: var(--fg-primary);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
  }

  .modal[open] {
    display: flex;
    flex-direction: column;
    animation: agp-rise-in var(--dur-base) var(--ease-out);
  }

  /* The literal value is a fallback for engines that do not yet inherit
     custom properties into ::backdrop; the token line wins where they do. */
  .modal::backdrop {
    background-color: rgba(16, 22, 26, 0.44);
    background-color: var(--bg-overlay);
    animation: agp-fade-in var(--dur-base) var(--ease-out);
  }

  .modal--sm { inline-size: min(100% - var(--space-8), 26rem); }
  .modal--lg { inline-size: min(100% - var(--space-8), 52rem); }

  .modal__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-block-end: 1px solid var(--border-subtle);
    flex: 0 0 auto;
  }

  .modal__title {
    font-size: var(--text-md);
    line-height: var(--lh-md);
    font-weight: var(--weight-semibold);
  }

  .modal__desc {
    margin-block-start: var(--space-1);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
  }

  .modal__body {
    padding: var(--space-5);
    overflow-y: auto;
    flex: 1 1 auto;
    min-block-size: 0;
  }

  .modal__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border-block-start: 1px solid var(--border-subtle);
    background-color: var(--bg-surface);
    flex: 0 0 auto;
  }

  .modal__footer--split {
    justify-content: space-between;
  }

  /* ======================================================================
     Drawer
     ====================================================================== */
  .drawer {
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    z-index: var(--z-drawer);
    display: flex;
    flex-direction: column;
    inline-size: min(100%, 30rem);
    background-color: var(--bg-surface);
    border-inline-start: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-lg);
    transform: translateX(100%);
    visibility: hidden;
    transition: transform var(--dur-slow) var(--ease-out),
                visibility var(--dur-slow) var(--ease-out);
  }

  .drawer[data-state="open"] {
    transform: translateX(0);
    visibility: visible;
  }

  [dir="rtl"] .drawer { transform: translateX(-100%); }
  [dir="rtl"] .drawer[data-state="open"] { transform: translateX(0); }

  .drawer--start {
    inset-inline-end: auto;
    inset-inline-start: 0;
    border-inline-start: 0;
    border-inline-end: 1px solid var(--border-subtle);
    transform: translateX(-100%);
  }

  .drawer--start[data-state="open"] { transform: translateX(0); }
  .drawer--wide { inline-size: min(100%, 44rem); }

  .drawer__scrim {
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-drawer) - 1);
    background-color: var(--bg-overlay);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-base) var(--ease-out),
                visibility var(--dur-base) var(--ease-out);
  }

  .drawer__scrim[data-state="open"] {
    opacity: 1;
    visibility: visible;
  }

  .drawer__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-block-end: 1px solid var(--border-subtle);
    flex: 0 0 auto;
  }

  .drawer__title {
    font-size: var(--text-md);
    line-height: var(--lh-md);
    font-weight: var(--weight-semibold);
  }

  .drawer__body {
    padding: var(--space-5);
    overflow-y: auto;
    overscroll-behavior: contain;
    flex: 1 1 auto;
    min-block-size: 0;
  }

  .drawer__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border-block-start: 1px solid var(--border-subtle);
    flex: 0 0 auto;
  }

  /* ======================================================================
     Toast
     ====================================================================== */
  .toast-region {
    position: fixed;
    inset-block-end: var(--space-5);
    inset-inline-end: var(--space-5);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    inline-size: min(100% - var(--space-8), 24rem);
    pointer-events: none;
  }

  .toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background-color: var(--bg-raised);
    border: 1px solid var(--border-default);
    border-inline-start: 2px solid var(--fg-muted);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
    animation: agp-rise-in var(--dur-base) var(--ease-out);
  }

  .toast[data-state="leaving"] {
    animation: agp-rise-out var(--dur-fast) var(--ease-in-out) forwards;
  }

  .toast__content { min-inline-size: 0; flex: 1 1 auto; }

  .toast__title {
    font-size: var(--text-base);
    line-height: var(--lh-base);
    font-weight: var(--weight-semibold);
  }

  .toast__body {
    margin-block-start: var(--space-1);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    text-wrap: pretty;
  }

  /* The tone, in a word. The four tones used to differ in exactly one
     declaration — the border colour below — and in nothing else: no icon, no
     word, no shape, no weight. A failure and a success were the same
     rectangle in two colours, which is no difference at all on a monochrome
     display, with a colour-vision deficiency, or through a screen reader
     (which reads none of it). app.js writes Note / Done / Warning / Error
     here on every toast, so the tone survives every one of those. Set in the
     muted ink rather than the tone's own colour on purpose: the word IS the
     second channel, and colouring it would make it the first one again. */
  .toast__tone {
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--fg-muted);
  }

  /* app.js builds `.toast`, `.toast--<tone>`, `__content`, `__tone`, `__title`
     and `__body`, and nothing else — so the tone is one line each. */
  .toast--success { border-inline-start-color: var(--success); }
  .toast--warning { border-inline-start-color: var(--warning); }
  .toast--danger  { border-inline-start-color: var(--danger); }
  .toast--info    { border-inline-start-color: var(--info); }

  @media (max-width: 640px) {
    .toast-region {
      inset-inline: var(--space-3);
      inset-block-end: var(--space-3);
      inline-size: auto;
    }
  }

  /* ======================================================================
     Notice
     ====================================================================== */
  /* Drawn in the hairline, like every other framed block. chat.css restates
     this one as `--artifact-line` for the transcript; that is the same colour
     under the answer's own name, and until now the two disagreed. */
  .notice {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
    font-size: var(--text-base);
    line-height: var(--lh-base);
  }

  .notice__icon { flex: 0 0 auto; margin-block-start: 1px; }
  .notice__content { min-inline-size: 0; flex: 1 1 auto; }
  .notice__title { font-weight: var(--weight-semibold); }
  .notice__body { color: inherit; opacity: 0.9; text-wrap: pretty; }
  .notice__actions { display: flex; gap: var(--space-2); margin-block-start: var(--space-2); }

  .notice--accent  { background-color: var(--accent-subtle);  border-color: var(--accent-border);  color: var(--accent-text); }
  .notice--success { background-color: var(--success-subtle); border-color: var(--success-border); color: var(--success-text); }
  .notice--warning { background-color: var(--warning-subtle); border-color: var(--warning-border); color: var(--warning-text); }
  .notice--danger  { background-color: var(--danger-subtle);  border-color: var(--danger-border);  color: var(--danger-text); }
  .notice--info    { background-color: var(--info-subtle);    border-color: var(--info-border);    color: var(--info-text); }

  /* ======================================================================
     Skeleton
     ====================================================================== */
  .skeleton {
    display: block;
    background-color: var(--bg-inset);
    border-radius: var(--radius-control);
    animation: agp-pulse 1.6s var(--ease-in-out) infinite;
  }

  .skeleton--text   { block-size: var(--lh-base); }
  .skeleton--title  { block-size: var(--lh-lg); border-radius: var(--radius-control); }
  .skeleton--circle { border-radius: var(--radius-pill); aspect-ratio: 1; block-size: var(--space-6); }
  .skeleton--block  { block-size: var(--space-16); border-radius: var(--radius-card); }

  /* A loading row at the loaded row's true geometry, so nothing reflows when
     the data lands. The height is derived from the same tokens the table row
     is built from — one line of --lh-sm, a --skeleton-cell-py pad each side,
     and the 1px rule underneath — never from a guessed pixel value. Somewhere
     that is not a table (the command palette's result rows) restates
     --skeleton-row-h with its own row height instead. */
  .skeleton-row {
    --skeleton-cell-py: var(--space-3);
    --skeleton-row-h: calc(var(--lh-sm) + (2 * var(--skeleton-cell-py)) + 1px);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    block-size: var(--skeleton-row-h);
    padding-inline: var(--space-3);
    border-block-end: 1px solid var(--border-subtle);
  }

  .skeleton-row:last-child { border-block-end: 0; }

  /* Mirrors .table--dense, whose rows it stands in for. */
  .skeleton-row--dense { --skeleton-cell-py: var(--space-2); }

  /* The command palette's result rows: .menu-item geometry — its control
     height, its inline padding, and no rule between rows. */
  .skeleton-row--menu {
    --skeleton-row-h: var(--control-h-md);
    padding-inline: var(--space-2);
    border-block-end: 0;
  }

  /* The bars sit at text height inside the row, like the cell content does. */
  .skeleton-row > .skeleton { block-size: var(--text-sm); }

  /* A pulsing placeholder is motion for its own sake; hold it still. */
  @media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; opacity: 0.75; }
  }
}

@layer components {
  /* ── The Settings / Developer-options dialogs ───────────────────────────
     Left rail of sections, one visible panel. Rows are label-left,
     control-right; notes are the quiet paragraph under the row they explain. */
  .settings .settings__body {
    display: grid;
    grid-template-columns: 11rem minmax(0, 1fr);
    min-block-size: 24rem;
    overflow: hidden;
  }
  .settings__rail {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--space-3) var(--space-2);
    border-inline-end: 1px solid var(--border-subtle);
    background: var(--bg-sunken);
  }
  .settings__tab {
    display: flex; align-items: center; gap: var(--space-2);
    padding: var(--space-2); border: 0; border-radius: var(--radius-control);
    background: transparent; color: var(--fg-secondary);
    font: inherit; font-size: var(--text-sm); text-align: left; cursor: pointer;
  }
  .settings__tab:hover { background: var(--bg-hover); color: var(--fg-primary); }
  .settings__tab.is-active { background: var(--bg-selected); color: var(--fg-primary); font-weight: var(--weight-medium); }
  .settings__panels { overflow-y: auto; padding: var(--space-4) var(--space-5); }
  .settings__panels--single { max-block-size: 34rem; }
  .settings__heading {
    margin: var(--space-5) 0 var(--space-2);
    font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--fg-primary);
  }
  .settings__panel > .settings__heading:first-child { margin-block-start: 0; }
  .settings__heading--caps {
    font-size: var(--text-xs); letter-spacing: var(--tracking-caps);
    text-transform: uppercase; color: var(--fg-faint);
  }
  .settings__row {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-2) 0;
    border-block-end: 1px solid var(--border-subtle);
  }
  .settings__label { font-size: var(--text-sm); color: var(--fg-secondary); display: inline-flex; align-items: center; gap: var(--space-2); }
  .settings__value { font-size: var(--text-sm); color: var(--fg-primary); }
  .settings__note {
    margin: var(--space-2) 0 0; font-size: var(--text-xs); line-height: var(--lh-xs);
    color: var(--fg-faint);
  }
  .settings__note--tight { margin-block-start: var(--space-1); }
  /* The answer-font sample. It exists so the control has a visible effect
     while the dialog is open: answers are the only prose that takes
     --font-prose, and on a page with no answers a face change would
     otherwise be imperceptible — the same "worked in the computed styles,
     did nothing a person could see" failure the text-size ramp had. */
  .settings__preview {
    margin: var(--space-3) 0 var(--space-1);
    padding: var(--space-3) var(--space-4);
    background: var(--bg-sunken);
    border-radius: var(--radius-control);
    font-family: var(--font-prose, inherit);
    font-size: var(--text-md);
    line-height: var(--lh-md);
    color: var(--fg-primary);
  }
  .settings__select { max-inline-size: 16rem; }
  .toggle.is-disabled { opacity: 0.55; cursor: not-allowed; }

  /* The microphone picker and its test. The meter is a sunken track whose bar
     is driven by voice.js — a scaleX transform repainted per frame from the
     live level, never a transition, so data-motion="off" (which zeroes
     transitions globally in tokens.css) has nothing left to animate: the bar
     simply shows the current level. */
  .settings__mic {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
  }
  .settings__meter {
    margin-block-start: var(--space-2);
    block-size: var(--space-2);
    background: var(--bg-sunken);
    border-radius: var(--radius-pill);
    overflow: hidden;
  }
  .settings__meter-bar {
    block-size: 100%;
    background: var(--accent);
    border-radius: inherit;
    transform: scaleX(0);
    transform-origin: left;
  }

  /* Developer options extras. */
  .devtools__roles { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-2) 0 var(--space-4); }
  .devtools__role {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--border-default); border-radius: var(--radius-pill);
    background: var(--bg-surface); color: var(--fg-secondary);
    font: inherit; font-size: var(--text-sm); cursor: pointer;
  }
  .devtools__role:hover { border-color: var(--accent-border); color: var(--fg-primary); }
  .devtools__role.is-active { background: var(--accent-subtle); border-color: var(--accent-border); color: var(--accent-text); }
  .devtools__preview { margin: var(--space-4) 0; }
  .devtools__preview-card { max-inline-size: 24rem; }

  @media (max-width: 640px) {
    .settings .settings__body { grid-template-columns: 1fr; }
    .settings__rail { flex-direction: row; overflow-x: auto; border-inline-end: 0; border-block-end: 1px solid var(--border-subtle); }
  }
}

@layer components {
  /* ── The data-display kit ────────────────────────────────────────────────
     Ported from the reference build's showcase (rowcard · copy-cell ·
     states · callout), re-cut onto this system's tokens. The row-card
     collapse itself lives in surfaces.css: it has to re-point rules the
     surfaces layer owns (cell padding, the .table--runs column floor), and a
     components-layer rule can never win that argument. */

  /* ── Copy cell ──────────────────────────────────────────────────────────
     An identifier worth quoting (a trace id, a run id, a VIN) gets a quiet
     icon-only button beside it. app.js owns the click: one delegated
     `data-action="copy-id"` case reads `data-copy` and answers with a toast.
     The button is always visible — a hover-reveal control does not exist on
     a touch screen and is undiscoverable everywhere else. */
  .copy-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-inline-size: 0;
    max-inline-size: 100%;
  }

  .copy-cell__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    inline-size: var(--space-5);
    block-size: var(--space-5);
    padding: 0;
    border: 0;
    border-radius: var(--radius-control);
    background-color: transparent;
    color: var(--fg-faint);
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-out),
      background-color var(--dur-fast) var(--ease-out);
  }

  .copy-cell__btn:hover,
  .copy-cell__btn:focus-visible {
    color: var(--accent-text);
    background-color: var(--bg-hover);
  }

  /* A fingertip needs the full control height; a pointer does not. The rows
     that carry these collapse to row cards on phones anyway, so the larger
     target never inflates a dense desktop table. */
  @media (pointer: coarse) {
    .copy-cell__btn {
      inline-size: var(--control-h-lg);
      block-size: var(--control-h-lg);
    }
  }

  /* ── Empty state · error state ──────────────────────────────────────────
     One shape for "nothing here" and one for "this failed", product-wide.
     The empty state says why it is empty and offers the action that would
     fill it; the error state says what failed, offers the retry, and prints
     the correlation id to quote — an error a reader cannot report is an
     error that gets reported as "it broke".
     Consumers: components/empty_state.html · components/error_state.html.
     chat.css's `.empty` (the assistant's opening screen) is a different
     thing and keeps its own recipe. */
  .empty-state {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--space-2);
    max-inline-size: 46ch;
    margin-inline: auto;
    padding: var(--space-10) var(--space-4);
    text-align: center;
  }

  .empty-state__figure {
    display: grid;
    place-items: center;
    inline-size: var(--space-8);
    block-size: var(--space-8);
    margin-block-end: var(--space-1);
    background-color: var(--bg-sunken);
    border-radius: var(--radius-card);
    color: var(--fg-muted);
  }

  .empty-state__title {
    color: var(--fg-primary);
    font-size: var(--text-md);
    line-height: var(--lh-md);
    font-weight: var(--weight-semibold);
  }

  .empty-state__body {
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    text-wrap: pretty;
  }

  .empty-state__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-2);
    margin-block-start: var(--space-2);
  }

  .empty-state--compact { padding-block: var(--space-6); }

  .error-state {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--space-2);
    max-inline-size: 52ch;
    margin-inline: auto;
    padding: var(--space-8) var(--space-4);
    text-align: center;
  }

  .error-state__figure {
    display: grid;
    place-items: center;
    inline-size: var(--space-8);
    block-size: var(--space-8);
    margin-block-end: var(--space-1);
    background-color: var(--danger-subtle);
    border-radius: var(--radius-card);
    color: var(--danger-text);
  }

  .error-state__title {
    color: var(--fg-primary);
    font-size: var(--text-md);
    line-height: var(--lh-md);
    font-weight: var(--weight-semibold);
  }

  .error-state__body {
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    text-wrap: pretty;
  }

  .error-state__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-2);
    margin-block-start: var(--space-2);
  }

  /* The id to quote. `user-select: all` so one press takes the whole thing;
     the copy button beside it does the same with one less gesture. */
  .error-state__corr {
    margin-block-start: var(--space-1);
    color: var(--fg-faint);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    user-select: all;
  }

  .error-state--compact { padding-block: var(--space-4); }

  /* ── Callout ────────────────────────────────────────────────────────────
     The promoted note: a titled, toned panel for the sentence a surface must
     not let the reader scroll past. `.notice` stays the flat one-liner; a
     callout carries a heading and a consequence. Tones keep the reference
     vocabulary (good / warn / crit) and resolve to this system's semantic
     tokens: good → success, warn → warning, crit → danger. There is no
     "info" callout — a neutral aside is a .notice, not a promotion. */
  .callout {
    padding: var(--space-3) var(--space-4);
    background-color: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
  }

  .callout__title {
    margin-block-end: var(--space-1);
    font-size: var(--text-base);
    line-height: var(--lh-base);
    font-weight: var(--weight-semibold);
    color: var(--fg-primary);
  }

  .callout__body {
    max-inline-size: var(--measure);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    color: var(--fg-secondary);
    text-wrap: pretty;
  }

  .callout[data-tone="good"] {
    background-color: var(--success-subtle);
    border-color: var(--success-border);
  }

  .callout[data-tone="good"] .callout__title { color: var(--success-text); }

  .callout[data-tone="warn"] {
    background-color: var(--warning-subtle);
    border-color: var(--warning-border);
  }

  .callout[data-tone="warn"] .callout__title { color: var(--warning-text); }

  .callout[data-tone="crit"] {
    background-color: var(--danger-subtle);
    border-color: var(--danger-border);
  }

  .callout[data-tone="crit"] .callout__title { color: var(--danger-text); }

  /* ── Result line ────────────────────────────────────────────────────────
     Ported from the reference build (`.result-line`, on nine of its
     surfaces). One quiet, tabular sentence directly above a collection,
     saying how much of it you are looking at — and, when the view caps its
     read, saying that out loud in the same sentence as the count. A silently
     truncated list is the failure this line exists to prevent; the pager at
     the bottom of a table cannot do the job, because it renders only where a
     view builds a Paginator and it sits past the fold besides.

     It is a live region (`role="status"` in the template, never here): a
     filter that changes the count announces the new count instead of
     silently redrawing. */
  .result-line {
    margin-block: var(--space-3) var(--space-2);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
    font-variant-numeric: tabular-nums;
    text-wrap: pretty;
  }

  .result-line b,
  .result-line strong {
    color: var(--fg-secondary);
    font-weight: var(--weight-semibold);
  }

  /* The disclosed cap — the half of the sentence that says the list is
     shorter than the truth. Warning-coloured because it is one. */
  .result-line__cap { color: var(--warning-text); }

  /* ── Working line ───────────────────────────────────────────────────────
     Ported from the reference build (`.workline`). One line that says what a
     run is doing, or what it did: live while it is still going — a pulsing
     dot and the stage it is in — and resolved once it has finished, as a
     single sentence of elapsed, stages, tools and model calls. The reference
     used it under a streaming transcript; here it heads the run-trace drawer
     on /activity, which previously could not say "this run is still going"
     at all.

     The pulse reuses `agp-pulse` (the skeleton's keyframe) rather than
     declaring a second one, and animates only where motion is welcome. */
  .workline {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-inline-size: 0;
    color: var(--fg-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-sm);
  }

  .workline__pulse {
    flex: 0 0 auto;
    inline-size: var(--space-2);
    block-size: var(--space-2);
    border-radius: var(--radius-pill);
    background-color: var(--fg-faint);
  }

  .workline[data-live="1"] .workline__pulse { background-color: var(--accent); }

  .workline__text {
    min-inline-size: 0;
    text-wrap: pretty;
  }

  .workline__stat {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: var(--lh-xs);
    font-variant-numeric: tabular-nums;
  }

  @media (prefers-reduced-motion: no-preference) {
    .workline[data-live="1"] .workline__pulse {
      animation: agp-pulse 1.6s var(--ease-in-out) infinite;
    }
  }
}
