/* ==========================================================================
   Raddad - shared components.

   Re-skins the primitives app.css already defines - .card, .btn, .pill,
   .field, inputs, tables, .callout, .empty, .stat, .kv, .switch, .radio-card,
   chips, .step - in the approved dashboard's visual language.

   WHY IT RESTYLES INSTEAD OF REPLACING
   Every one of these class names is load-bearing: app.js binds to .chips,
   .chip, .radio-card and [data-copy], and campaign_form.html reaches for
   .field-error, .char-count, .dependent-panel and [data-button-row] by name.
   Renaming any of them would be a functional change dressed up as a visual
   one. So the markup keeps its contract and only the paint changes.

   WHAT IS AND IS NOT HERE
   dashboard.css already defines .glass, .card, .btn, .empty, tables, .toast
   and the whole shell under `.raddad-dashboard`, and every authenticated page
   now loads it. Duplicating those here would tie on specificity and win by
   load order - silently overriding the approved look. So they were removed;
   this file only adds what the dashboard never needed.

   Scope: `.rd-ui`, carried by authenticated pages (with .rd-app) and by login
   and error pages (with .rd-auth). The dashboard carries none of the three, so
   nothing here can reach it.

   THE [hidden] CONTRACT - read before adding a display rule
   `hidden` is only a user-agent rule, so any author `display` defeats it and
   the element becomes permanently visible. app.css opts out for .callout,
   .btn, .dependent-panel and .form-errors; because this file loads *after*
   app.css, every display we set here must be opted out again at the bottom of
   this file. tests/test_batch_e_ui.py guards the app.css half of that;
   the block at the end of this file is the other half.
   ========================================================================== */


.rd-ui .field-hint code,
.rd-ui .callout code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .92em;
  padding: 1px 5px;
  border-radius: 6px;
  background: var(--rd-fill);
  color: var(--rd-text-2);
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */

.rd-ui .field { margin-bottom: 18px; }
.rd-ui .field:last-child { margin-bottom: 0; }

.rd-ui .field-label {
  display: block;
  margin-bottom: 7px;
  font-size: var(--rd-fs-sm);
  font-weight: 600;
  color: var(--rd-text-2);
  letter-spacing: .01em;
}
.rd-ui .field-required { color: var(--rd-red); margin-left: 2px; }
.rd-ui .field-optional {
  margin-left: 6px;
  font-weight: 500;
  color: var(--rd-dim);
  font-size: var(--rd-fs-xs);
}
.rd-ui .field-hint {
  margin-top: 6px;
  font-size: var(--rd-fs-sm);
  color: var(--rd-muted);
  line-height: 1.55;
}
.rd-ui .field-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 6px;
  min-height: 16px;
}
.rd-ui .field-error {
  font-size: var(--rd-fs-sm);
  color: var(--rd-red);
}
.rd-ui .char-count {
  font-size: var(--rd-fs-xs);
  color: var(--rd-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rd-ui .char-count.over-limit { color: var(--rd-red); font-weight: 600; }

/* ---- The one Raddad input material ----
   Every field on every page uses this: campaign form, connection, webhook,
   log filters, login. Navy glass, never black. Kept a step darker than the
   panel it sits in so it reads as a recess, without becoming a void. */
.rd-ui input[type="text"],
.rd-ui input[type="url"],
.rd-ui input[type="password"],
.rd-ui input[type="search"],
.rd-ui input[type="email"],
.rd-ui input[type="number"],
.rd-ui input[type="tel"],
.rd-ui input[type="date"],
.rd-ui select,
.rd-ui textarea,
.rd-ui .raddad-input {
  width: 100%;
  padding: 10px 13px;
  border-radius: var(--rd-input-radius);
  border: 1px solid var(--rd-input-border);
  background: var(--rd-input-bg);
  box-shadow: var(--rd-input-shadow);
  color: var(--rd-text);
  font: inherit;
  font-size: var(--rd-fs);
  line-height: 1.5;
  transition: border-color .16s ease, background .16s ease, box-shadow .16s ease;
}
.rd-ui textarea { min-height: 92px; resize: vertical; }

/* The browser draws its own calendar-picker icon inside a date field, and
   defaults to a dark glyph that disappears on this dark fill without this. */
.rd-ui input[type="date"] { color-scheme: dark; }

.rd-ui input::placeholder,
.rd-ui textarea::placeholder { color: var(--rd-input-placeholder); }

/* Hover lifts the border only - the fill must not flicker under the cursor. */
.rd-ui input:hover:not(:disabled):not([readonly]),
.rd-ui select:hover:not(:disabled),
.rd-ui textarea:hover:not(:disabled):not([readonly]) {
  border-color: var(--rd-input-border-hover);
}

/* Restrained: a ring, not a neon halo. */
.rd-ui input:focus,
.rd-ui select:focus,
.rd-ui textarea:focus {
  outline: none;
  border-color: var(--rd-input-focus-border);
  box-shadow: var(--rd-input-focus-shadow);
}

/* Border only. Filling the field red makes its own text unreadable. */
.rd-ui .field.has-error input,
.rd-ui .field.has-error textarea,
.rd-ui .field.has-error select {
  border-color: var(--rd-input-error-border);
}

/* Readonly fields hold values the operator has to read and copy - the
   callback URL, the refresh command. Muted, never blacked out.

   Matched by [readonly] only, deliberately. The :read-only pseudo-class also
   matches checkboxes and radios (they are never "editable"), so using it here
   restyled every hidden switch and radio-card input on the campaign form. */
.rd-ui input[readonly],
.rd-ui textarea[readonly] {
  color: var(--rd-text-2);
  border-color: var(--rd-hairline);
  background: color-mix(in srgb, var(--rd-input-bg) 78%, transparent);
}
.rd-ui input:disabled,
.rd-ui select:disabled,
.rd-ui textarea:disabled {
  opacity: .55;
  cursor: not-allowed;
  color: var(--rd-muted);
}

/* The native arrow is nearly invisible on a dark fill, so it is drawn. The
   longhand background-* properties are used deliberately: the shorthand would
   drop the fill set above. */
.rd-ui select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b0bed9' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 15px;
}

.rd-ui .input-mono,
.rd-ui .td-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--rd-fs-sm);
  letter-spacing: -.01em;
}
.rd-ui .td-mono { color: var(--rd-muted); }

.rd-ui .copy-field {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.rd-ui .copy-field input { flex: 1 1 auto; min-width: 0; }
.rd-ui .copy-field .btn { flex: 0 0 auto; }

/* On a narrow screen the button leaves the input too short to show a whole
   callback URL, and these are fields the operator has to read before copying.
   Giving the input the full row and dropping the button beneath buys back the
   ~70px the button was taking. */
@media (max-width: 560px) {
  .rd-ui .copy-field { flex-wrap: wrap; }
  .rd-ui .copy-field .btn { width: 100%; }
}

/* ---- Switch ---- */

.rd-ui .switch {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: var(--rd-fs);
  color: var(--rd-text-2);
  user-select: none;
}
/* Visually hidden but still focusable. app.css used `display: none` here,
   which removes the checkbox from the tab order; keyboard users could not
   reach any switch on the campaign form. The visible focus indicator is on
   .switch-track below. */
.rd-ui .switch input {
  position: absolute;
  display: block;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.rd-ui .switch-track {
  position: relative;
  flex: 0 0 auto;
  width: 40px;
  height: 23px;
  border-radius: 999px;
  background: var(--rd-input-bg);
  border: 1px solid var(--rd-input-border);
  transition: background .18s ease, border-color .18s ease;
}
/* A contact shadow and a thin top highlight, so the thumb reads as a small
   raised disc rather than a flat dot - the same restrained depth cue the
   rest of the material system uses, just at control scale. */
.rd-ui .switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--rd-muted);
  box-shadow: 0 1px 3px rgba(3, 12, 30, .35), inset 0 1px 0 rgba(255, 255, 255, .25);
  transition: transform .18s ease, background .18s ease;
}
.rd-ui .switch input:checked + .switch-track {
  background: rgba(61, 145, 255, .34);
  border-color: rgba(61, 145, 255, .5);
}
.rd-ui .switch input:checked + .switch-track .switch-thumb {
  transform: translateX(17px);
  background: #fff;
}
.rd-ui .switch input:focus-visible + .switch-track {
  box-shadow: 0 0 0 3px rgba(61, 145, 255, .2);
}

/* ---- Radio cards ---- */

.rd-ui .radio-group {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px;
}
.rd-ui .radio-card {
  display: block;
  padding: 12px 14px;
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
  cursor: pointer;
  transition: border-color .16s ease, background .16s ease;
}
.rd-ui .radio-card:hover { border-color: var(--rd-border-soft); }
/* Visually hidden but focusable, and the focus indicator is on the card. */
.rd-ui .radio-card input {
  position: absolute;
  display: block;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.rd-ui .radio-card:focus-within {
  border-color: var(--rd-input-focus-border);
  box-shadow: var(--rd-input-focus-shadow);
}
/* Selected is a step up in the same blue family - brighter glass and a blue
   border with a faint inner accent, not a saturated panel of its own. Both
   selectors are needed: app.js sets .selected, and :has covers the first
   paint before its listener runs. */
.rd-ui .radio-card.selected,
.rd-ui .radio-card:has(input:checked) {
  border-color: rgba(75, 145, 255, .55);
  background:
    linear-gradient(145deg, rgba(61, 145, 255, .16), rgba(61, 145, 255, .07)),
    var(--rd-nested);
  box-shadow: inset 1px 1px 0 rgba(255, 255, 255, .06);
}
.rd-ui .rc-title {
  display: block;
  font-size: var(--rd-fs);
  font-weight: 600;
  color: var(--rd-text);
}
.rd-ui .rc-desc {
  margin-top: 3px;
  font-size: var(--rd-fs-sm);
  color: var(--rd-muted);
  line-height: 1.5;
}

/* ---- Chips (keyword editor) ---- */

.rd-ui .chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 8px 10px;
  border-radius: var(--rd-input-radius);
  border: 1px solid var(--rd-input-border);
  background: var(--rd-input-bg);
  box-shadow: var(--rd-input-shadow);
  cursor: text;
}
/* The chip input's own focus ring is suppressed below (it sits flush inside
   the container, so its own box-shadow would clip against the chips beside
   it) - the container carries the indicator instead, same pattern as
   .radio-card:focus-within above. */
.rd-ui .chips:focus-within {
  border-color: var(--rd-input-focus-border);
  box-shadow: var(--rd-input-focus-shadow);
}
.rd-ui .chips input {
  flex: 1 1 130px;
  min-width: 130px;
  width: auto;
  border: 0;
  background: transparent;
  padding: 3px 2px;
  box-shadow: none;
}
.rd-ui .chips input:focus { box-shadow: none; background: transparent; }
.rd-ui .chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px 4px 10px;
  border-radius: 999px;
  background: rgba(61, 145, 255, .16);
  border: 1px solid rgba(61, 145, 255, .28);
  font-size: var(--rd-fs-sm);
  color: var(--rd-text);
  transition: background .14s ease, border-color .14s ease;
}
.rd-ui .chip:hover { background: rgba(61, 145, 255, .22); border-color: rgba(61, 145, 255, .4); }
.rd-ui .chip button {
  border: 0;
  background: none;
  color: var(--rd-muted);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  padding: 0 1px;
}
.rd-ui .chip button:hover { color: var(--rd-text); }

.rd-ui .keyword-list { display: flex; flex-wrap: wrap; gap: 6px; }

/* --------------------------------------------------------------------------
   Pills and status
   -------------------------------------------------------------------------- */

.rd-ui .pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: var(--rd-fs-xs);
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid transparent;
}
.rd-ui .pill .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.rd-ui .pill-ok {
  background: rgba(50, 230, 161, .13);
  border-color: rgba(50, 230, 161, .3);
  color: var(--rd-green);
}
.rd-ui .pill-warn {
  background: rgba(251, 191, 36, .13);
  border-color: rgba(251, 191, 36, .3);
  color: var(--rd-amber);
}
.rd-ui .pill-err {
  background: rgba(248, 113, 113, .13);
  border-color: rgba(248, 113, 113, .3);
  color: var(--rd-red);
}
.rd-ui .pill-info {
  background: rgba(61, 145, 255, .13);
  border-color: rgba(61, 145, 255, .3);
  color: var(--rd-blue);
}
.rd-ui .dot-pulse { animation: rd-pulse 2s ease-in-out infinite; }
@keyframes rd-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .rd-ui .dot-pulse { animation: none; }
  .rd-ui .switch-track,
  .rd-ui .switch-thumb,
  .rd-ui .chip,
  .rd-ui .radio-card,
  .rd-ui .media-item {
    transition-duration: 1ms;
  }
  .rd-ui .media-item:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   Callouts
   -------------------------------------------------------------------------- */

.rd-ui .callout {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  padding: 13px 15px;
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
  font-size: var(--rd-fs);
  line-height: 1.6;
  color: var(--rd-text-2);
  margin-bottom: var(--rd-gap);
}
.rd-ui .callout:last-child { margin-bottom: 0; }
.rd-ui .callout-icon { flex: 0 0 auto; line-height: 1.5; }
.rd-ui .callout strong { color: var(--rd-text); font-weight: 650; }
.rd-ui .callout a { color: var(--rd-blue); }
.rd-ui .callout-ok {
  background: rgba(50, 230, 161, .09);
  border-color: rgba(50, 230, 161, .24);
}
.rd-ui .callout-warn {
  background: rgba(251, 191, 36, .09);
  border-color: rgba(251, 191, 36, .24);
}
.rd-ui .callout-err {
  background: rgba(248, 113, 113, .09);
  border-color: rgba(248, 113, 113, .26);
}
.rd-ui .callout-info {
  background: rgba(61, 145, 255, .09);
  border-color: rgba(61, 145, 255, .22);
}

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   Media thumbnail
   A linked Instagram post's own thumbnail (Campaign.media_thumbnail_url), or a
   neutral placeholder when Instagram gave none. Shared rather than owned by
   any one page: Campaigns and Comments both point at the same post data and
   must render it identically, not approximately.
   -------------------------------------------------------------------------- */
.rd-ui .media-thumb,
.rd-ui .media-thumb-empty {
  flex: 0 0 auto;
  width: 74px;
  height: 74px;
  border-radius: var(--rd-radius-sm);
  object-fit: cover;
  background: var(--rd-input-bg);
  border: 1px solid var(--rd-nested-border);
}
.rd-ui .media-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10.5px;
  letter-spacing: .03em;
  color: var(--rd-dim);
  text-align: center;
  padding: 6px;
}


/* --------------------------------------------------------------------------
   Empty states
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   Stats and key/value rows
   -------------------------------------------------------------------------- */


.rd-ui .kv {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 10px 12px;
  border-radius: var(--rd-radius-xs);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
  margin-bottom: 8px;
  font-size: var(--rd-fs-sm);
  flex-wrap: wrap;
}
.rd-ui .kv:last-of-type { margin-bottom: 0; }
.rd-ui .kv-key { color: var(--rd-text-2); min-width: 0; word-break: break-word; }
.rd-ui .kv-val {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--rd-text);
  text-align: right;
  /* A flex item's default min-width is its content's width, not 0 - without
     this, long unbroken values (or a long .card-link child) refuse to
     shrink below that and push the row, and the page, wider. */
  min-width: 0;
  /* For plain text values (error messages, long tokens); a nested .card-link
     truncates on its own instead. */
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   Numbered steps (campaign form)
   -------------------------------------------------------------------------- */

.rd-ui .step { display: flex; gap: 15px; align-items: flex-start; }
.rd-ui .step-num {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--rd-fill);
  border: 1px solid var(--rd-border-soft);
  font-size: var(--rd-fs-sm);
  font-weight: 700;
  color: var(--rd-text);
}
.rd-ui .step-body { flex: 1 1 auto; min-width: 0; }
.rd-ui .step-title {
  font-size: var(--rd-fs-md);
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--rd-text);
}
.rd-ui .step-desc {
  margin: 4px 0 16px;
  font-size: var(--rd-fs-sm);
  color: var(--rd-muted);
  line-height: 1.6;
}
.rd-ui .section-heading-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.rd-ui .feature-disabled { opacity: .55; }

/* Revealed under a step's own toggle (Campaign Builder's public-reply/DM/
   follow-gate/final-link sections) - had no approved-system rule at all
   before the Phase 1 closeout, so it rendered as app.css's flat
   rgba(0,0,0,.08) panel underneath every other approved surface around it. */
.rd-ui .dependent-panel {
  padding: 15px;
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
}

.rd-ui .workflow-map {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 15px;
  margin-bottom: var(--rd-gap);
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
  font-size: var(--rd-fs-sm);
  color: var(--rd-text-2);
}
.rd-ui .workflow-map b { color: var(--rd-blue); font-weight: 700; }

.rd-ui .optional-settings {
  margin-bottom: 18px;
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
  padding: 0 14px;
}
.rd-ui .optional-settings > summary {
  padding: 12px 0;
  cursor: pointer;
  font-size: var(--rd-fs);
  font-weight: 600;
  color: var(--rd-text-2);
}
.rd-ui .optional-settings[open] > summary {
  border-bottom: 1px solid var(--rd-nested-border);
  margin-bottom: 14px;
}
.rd-ui .optional-settings > *:last-child { margin-bottom: 14px; }

/* ---- Repeatable button rows ---- */

.rd-ui .button-list { display: flex; flex-direction: column; gap: 8px; }
.rd-ui .button-list:not(:empty) { margin-bottom: 10px; }
.rd-ui .button-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.rd-ui .button-row .button-title { flex: 1 1 190px; min-width: 0; }
.rd-ui .button-row-action .button-title { flex: 1 1 150px; }
.rd-ui .button-row-action select { flex: 0 1 150px; width: auto; min-width: 0; }
.rd-ui .button-row-action .button-value { flex: 1 1 190px; min-width: 0; }

/* The chosen post, echoed back under the picker. A nested row inside the
   step panel - previously an inline flat grey card built from app.css's
   legacy --surface-2/--border, which is why this step looked like a
   different product. */
.rd-ui .selected-media {
  gap: 13px;
  margin-top: 14px;
  padding: 13px;
  border-radius: var(--rd-radius-sm);
  background: var(--rd-nested);
  border: 1px solid var(--rd-nested-border);
  box-shadow: inset 1px 1px 0 rgba(255, 255, 255, .04);
}
.rd-ui .selected-media img {
  border-radius: 9px;
  background: var(--rd-input-bg);
}

/* ---- Media picker ---- */

.rd-ui .media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 10px;
}
.rd-ui .media-item {
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-nested-border);
  background: var(--rd-nested);
  overflow: hidden;
  cursor: pointer;
  transition: border-color .16s ease, transform .16s ease;
}
.rd-ui .media-item:hover { border-color: var(--rd-border-soft); transform: translateY(-1px); }
.rd-ui .media-item.selected { border-color: rgba(61, 145, 255, .6); }
.rd-ui .media-item img {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  background: var(--rd-input-bg);
}
.rd-ui .media-item-body { padding: 8px 9px; }
.rd-ui .media-item-caption {
  font-size: var(--rd-fs-xs);
  color: var(--rd-text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rd-ui .media-item-type {
  margin-top: 3px;
  font-size: 10.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--rd-dim);
}

/* --------------------------------------------------------------------------
   Layout helpers
   -------------------------------------------------------------------------- */

.rd-ui .grid { display: grid; gap: var(--rd-gap); align-items: stretch; }
.rd-ui .grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
/* Card width sits between grid-2's and grid-4's, same reasoning as both:
   collapse by available space, not by a hand-picked breakpoint. app.css's
   .grid-3 never got this treatment and stayed a fixed repeat(3, 1fr), which
   is why Settings' three-card row held exactly 3 equal fixed-width columns
   down to its single legacy 860px breakpoint instead of narrowing like
   every other grid on the approved system. */
.rd-ui .grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.rd-ui .grid-4 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

/* One semantic pair, not two: app.css's .stat-value.ok/.err used the legacy
   --ok/--err colours (a different green/red than the approved system), so a
   status number rendered in a different green than a status pill three
   inches away. */
.rd-ui .stat-value.ok { color: var(--rd-green); }
.rd-ui .stat-value.err { color: var(--rd-red); }

/* A grid item carrying its own bottom margin is shortened by exactly that
   margin and then bottom-aligned, so cards sharing a row stop matching. The
   grid's own gap already provides the spacing. */
.rd-ui .grid > .card,
.rd-ui .grid > .stat,
.rd-ui .grid > .campaign-card { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Long-form copy inside panels (webhook setup steps, connection notes)
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   Toasts

   Same elevated-glass material as the top bar (raised weight, blurred,
   composited shadow) - a toast floats above the page the same way the bar
   does. Severity reads only through the left accent bar and the icon-less
   colour of that bar, matching how .callout carries severity, so a user who
   has learned one reads the other. Warn/err stay on screen with a manual
   dismiss instead of auto-clearing: app.js's `toast()` only starts the
   auto-dismiss timer for "ok" and default/info calls.
   -------------------------------------------------------------------------- */

.rd-ui .toast-wrap {
  position: fixed;
  bottom: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 100;
  max-width: min(380px, calc(100vw - 40px));
}
.rd-ui .toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 15px;
  border-radius: var(--rd-radius-sm);
  border: 1px solid var(--rd-border);
  border-left: 3px solid var(--rd-blue);
  background: var(--rd-glass-raised);
  -webkit-backdrop-filter: blur(var(--rd-blur)) saturate(125%);
  backdrop-filter: blur(var(--rd-blur)) saturate(125%);
  box-shadow: var(--rd-glass-shadow);
  color: var(--rd-text);
  font-size: var(--rd-fs);
  line-height: 1.5;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .rd-ui .toast { background: var(--rd-panel-2); }
}
.rd-ui .toast.ok { border-left-color: var(--rd-green); }
.rd-ui .toast.err { border-left-color: var(--rd-red); }
.rd-ui .toast.warn { border-left-color: var(--rd-amber); }

.rd-ui .toast-message { flex: 1 1 auto; min-width: 0; }

/* Only rendered for warn/err, which do not auto-dismiss. */
.rd-ui .toast-dismiss {
  flex: none;
  appearance: none;
  border: 0;
  background: none;
  margin: -3px -3px -3px 0;
  padding: 3px;
  border-radius: 6px;
  color: var(--rd-dim);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition: color .12s ease;
}
.rd-ui .toast-dismiss:hover { color: var(--rd-text); }
.rd-ui .toast-dismiss:focus-visible {
  outline: none;
  color: var(--rd-text);
  box-shadow: var(--rd-input-focus-shadow);
}

@media (prefers-reduced-motion: no-preference) {
  .rd-ui .toast { animation: rd-toast-in .2s ease; }
}
@keyframes rd-toast-in {
  from { opacity: 0; transform: translateX(14px); }
  to { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Icon + label buttons (Edit / Duplicate / Delete rows)
   The icon sits inline before the label; .btn is already inline-flex, so
   this only sizes the glyph. The trash glyph carries a stronger red than the
   quiet .btn-danger label so the destructive action reads at a glance.
   -------------------------------------------------------------------------- */

.rd-ui .btn-icon-label { gap: 6px; white-space: nowrap; }
.rd-ui .btn-icon-label .btn-ic {
  flex: none;
  width: 15px;
  height: 15px;
}
.rd-ui .btn-icon-label .btn-ic-delete { color: #f87171; }

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   [hidden] opt-outs - see the header of this file.

   `hidden` is only a user-agent rule, so any author `display` defeats it. Every
   display set above (and every one dashboard.css sets, since it loads first)
   has to be cancelled here or the campaign form breaks: the no-buttons warning
   would show on every campaign, "+ Add Button" would stay visible at the
   maximum, and the disabled step panels would never collapse.

   The catch-all first selector is the safety net; the named ones exist so this
   keeps working if a future rule raises specificity. Do not trim this block:
   an earlier automated pass removed part of it and left a selector list with
   no declaration, which disabled the lot.
   -------------------------------------------------------------------------- */

.rd-ui [hidden],
.rd-ui .btn[hidden],
.rd-ui .callout[hidden],
.rd-ui .card[hidden],
.rd-ui .chip[hidden],
.rd-ui .dependent-panel[hidden],
.rd-ui .empty[hidden],
.rd-ui .field[hidden],
.rd-ui .form-errors[hidden],
.rd-ui .kv[hidden],
.rd-ui .pill[hidden],
.rd-ui .radio-card[hidden],
.rd-ui .step[hidden],
.rd-ui .switch[hidden],
.rd-ui .table-wrap[hidden],
.rd-ui .toast[hidden],
.rd-ui [data-search-row][hidden],
.rd-ui [data-search-empty][hidden] { display: none !important; }
