@scope (alert-ui) {
  :where(:scope) {
    /* ── Colors ── */
    --alert-bg-default:       var(--a-bg-muted);
    --alert-fg-default:       var(--a-fg);
    --alert-border-default:   var(--a-border-subtle);
    --alert-icon-fg-default:  var(--a-fg-muted);

    /* ── Layout ── */
    --alert-radius-default:   var(--a-radius-md);
    --alert-px-default:       var(--a-space-3);
    --alert-py-default:       var(--a-space-2);
    --alert-gap-default:      var(--a-space-2);

    /* ── Typography ── */
    --alert-font-default:        var(--a-ui-size);
    --alert-line-height-default: var(--a-ui-line-height, 1.5);

    /* ── SPEC-006 dunning pattern tokens ── */
    --alert-amount-font-default: var(--a-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    --alert-meta-fg-default:     var(--a-fg-muted);
    --alert-actions-gap-default: var(--a-space-2);
    --alert-actions-mt-default:  var(--a-space-2);
  }

  :scope {
    /* ── Base ── */
    box-sizing: border-box;
    display: flex;
    /* `align-items: start` so the leading icon and close button anchor
       to the top — keeps the icon visually paired with the first line of
       text on multi-line alerts. The leading slot's own height = body
       line-height (below) means the icon optically centers on line 1. */
    align-items: start;
    gap: var(--alert-gap, var(--alert-gap-default));
    padding: var(--alert-py, var(--alert-py-default)) var(--alert-px, var(--alert-px-default));
    border: 1px solid var(--alert-border, var(--alert-border-default));
    border-radius: var(--alert-radius, var(--alert-radius-default));
    font-size: var(--alert-font, var(--alert-font-default));
    line-height: var(--alert-line-height, var(--alert-line-height-default));
    color: var(--alert-fg, var(--alert-fg-default));
    background: var(--alert-bg, var(--alert-bg-default));
  }

  /* ── Variants ── */
  :scope[variant="info"] {
    --alert-bg-default:      var(--a-info-muted);
    --alert-fg-default:      var(--a-info-text);
    --alert-border-default:  var(--a-info-border-subtle);
    --alert-icon-fg-default: var(--a-info-strong);
  }

  :scope[variant="success"] {
    --alert-bg-default:      var(--a-success-muted);
    --alert-fg-default:      var(--a-success-text);
    --alert-border-default:  var(--a-success-border-subtle);
    --alert-icon-fg-default: var(--a-success-strong);
  }

  :scope[variant="warning"] {
    --alert-bg-default:      var(--a-warning-muted);
    --alert-fg-default:      var(--a-warning-text);
    --alert-border-default:  var(--a-warning-border-subtle);
    --alert-icon-fg-default: var(--a-warning-strong);
  }

  :scope[variant="danger"] {
    --alert-bg-default:      var(--a-danger-muted);
    --alert-fg-default:      var(--a-danger-text);
    --alert-border-default:  var(--a-danger-border-subtle);
    --alert-icon-fg-default: var(--a-danger-strong);
  }

  /* `muted` and `neutral` are semantic aliases of the base — same tokens
     as the default variant, declared explicitly so the yaml enum and the
     CSS contract agree. Use cases: passive callouts (beta banners,
     tertiary notes) where the alert should read as quiet chrome rather
     than carrying tonal weight. */
  :scope[variant="muted"],
  :scope[variant="neutral"] {
    --alert-bg-default:      var(--a-bg-muted);
    --alert-fg-default:      var(--a-fg);
    --alert-border-default:  var(--a-border-subtle);
    --alert-icon-fg-default: var(--a-fg-muted);
  }

  /* ── Slots ── */
  :scope [slot="leading"] {
    flex-shrink: 0;
    color: var(--alert-icon-fg, var(--alert-icon-fg-default));
    /* Box height = one line of body so the icon optical-centers on the
       first line under `align-items: start`. */
    display: inline-flex;
    align-items: center;
    min-height: calc(var(--alert-font, var(--alert-font-default)) * var(--alert-line-height, var(--alert-line-height-default)));
    /* `ensure()` appends the leading-slot icon to the host, which
       puts it after any consumer-provided content in DOM order.
       Force it to the visual lead via flex `order` so the icon
       always reads first. */
    order: -1;
  }

  :scope [slot="content"] {
    flex: 1;
    min-width: 0;
  }

  /* Trailing action area — optional single button or small button group.
     flex-shrink:0 keeps it from collapsing; align-self:start anchors it
     to the first text line under the `align-items:start` root. */
  :scope [slot="action"] {
    flex-shrink: 0;
    align-self: start;
  }

  /* Close button is a `<button-ui icon="x" variant="ghost" size="sm">`
     stamped by alert.js — it brings its own focus ring, hover state,
     and transitions. Box height matches the leading slot so X and icon
     sit on the same first-line baseline. */
  :scope [slot="close"] {
    flex-shrink: 0;
    min-height: calc(var(--alert-font, var(--alert-font-default)) * var(--alert-line-height, var(--alert-line-height-default)));
  }

  /* ──────────────────────────────────────────────────────────────
     SPEC-006 — Dunning / Payment-Failed pattern
     ──────────────────────────────────────────────────────────────
     `pattern="dunning"` switches the alert into a billing notice
     layout. The default alert is a single-row flex container; the
     dunning mode keeps the leading icon and close button in a
     "header" row but stacks the content + actions vertically below.

     We achieve this by switching the host to `flex-wrap: wrap` and
     assigning `order` per slot. The standard alert children authored
     in arbitrary DOM order (buttons before content is the common
     consumer mistake) are visually re-ordered by these rules so the
     final layout always reads icon → content → actions → close. */

  :scope[pattern="dunning"] {
    flex-wrap: wrap;
    row-gap: var(--alert-actions-mt, var(--alert-actions-mt-default));
    /* Use align-items: flex-start so the leading icon hugs the top
       of the message regardless of how tall the content + actions
       column grows. */
    align-items: flex-start;
  }

  /* Visual order — icon first, then content, then actions, then
     close. Source DOM order is irrelevant under these rules. The
     `order` integers leave room (10/20/30/40) for future expansion
     without renumbering. */
  :scope[pattern="dunning"] [slot="leading"]   { order: 10; }
  :scope[pattern="dunning"] [slot="content"]   { order: 20; }
  :scope[pattern="dunning"] [slot="actions"]   { order: 30; }
  :scope[pattern="dunning"] [slot="close"]     { order: 40; }

  :scope[pattern="dunning"] [data-dunning-amount] {
    font-family: var(--alert-amount-font, var(--alert-amount-font-default));
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    /* Inherit color from the variant; tabular-nums + monospace make
       the amount the scannable focal datum. */
  }

  :scope[pattern="dunning"] [data-dunning-meta] {
    color: var(--alert-meta-fg, var(--alert-meta-fg-default));
    font-size: calc(var(--alert-font, var(--alert-font-default)) * 0.9375);
  }

  :scope[pattern="dunning"] [data-dunning-title] {
    display: inline-block;
    margin-inline-end: var(--a-space-1, 0.25rem);
  }

  /* Actions slot — sits below the content as a row of buttons.
     `flex-basis: 100%` combined with the `order: 30` rule pushes
     the actions onto their own row after the content row. The
     padding-inline-start indents the buttons under the message
     column (so they left-align with the body text, not the leading
     icon). */
  :scope[pattern="dunning"] [slot="actions"] {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: var(--alert-actions-gap, var(--alert-actions-gap-default));
    padding-inline-start: calc(var(--alert-font, var(--alert-font-default)) + var(--alert-gap, var(--alert-gap-default)));
  }

  /* Lenient — when a bare button-ui carries data-dunning-action but
     no explicit slot=actions, give it the same visual order as the
     actions group so authoring oversights don't break layout. */
  :scope[pattern="dunning"] > [data-dunning-action]:not([slot]) {
    order: 30;
    flex-basis: 100%;
  }
}
