/* dash.css — admin chrome built on top of widget.css.
 * Tighter spacing than the supporter side; same broadside aesthetic.
 */

.dash {
    background: var(--sc-paper);
    color: var(--sc-ink);
}

/* ---- Nav bar ------------------------------------------------------------ */
.dash-nav {
    background: var(--sc-panel);
    border-bottom: 1px solid var(--sc-rule);
    padding: 0;
    position: sticky;
    top: 0;
    z-index: 10;
    box-shadow: 0 1px 0 rgba(26,26,26,0.04);
}
.dash-nav__inner {
    max-width: 1180px;
    margin: 0 auto;
    padding: 0 24px;
    display: flex;
    align-items: stretch;
    gap: 4px;
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}
.dash-nav__link {
    color: var(--sc-moss);
    text-decoration: none;
    padding: 14px 14px;
    border-bottom: 2px solid transparent;
    transition: color 140ms ease, border-color 140ms ease;
}
.dash-nav__link:hover { color: var(--sc-primary); }
.dash-nav__link--active {
    color: var(--sc-primary);
    border-bottom-color: var(--sc-accent);
    font-weight: 700;
}
.dash-nav__link--quiet { color: var(--sc-moss); }
/* #57: pending-letters count badge on the Review nav item. A small filled pill
   so "N waiting" reads at a glance without shouting; hidden entirely at 0 (the
   template omits it). Uses --sc-accent so it pops against the moss nav text. */
.dash-nav__badge {
    display: inline-block;
    min-width: 16px;
    padding: 1px 5px;
    margin-left: 2px;
    border-radius: 999px;
    background: var(--sc-accent);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 1.5;
    text-align: center;
    vertical-align: middle;
    letter-spacing: 0;
}
.dash-nav__spacer { flex: 1; }
.dash-nav__form { margin: 0; display: flex; align-items: stretch; }
.dash-nav__signout {
    appearance: none;
    background: transparent;
    border: none;
    border-left: 1px solid var(--sc-rule);
    padding: 14px 18px;
    cursor: pointer;
    font-family: inherit;
    font-size: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: var(--sc-stamp);
}
.dash-nav__signout:hover { background: rgba(192,74,44,0.06); }

/* ---- Page shell --------------------------------------------------------- */
.dash-shell {
    max-width: 1180px;
    margin: 0 auto;
    padding: 36px 24px 64px;
}
.dash-foot {
    max-width: 1180px;
    margin: 60px auto 0;
    padding: 22px 24px 32px;
    border-top: 1px solid var(--sc-rule);
    display: flex;
    justify-content: space-between;
    font-family: var(--sc-font-mono);
    font-size: 10.5px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--sc-moss);
}

.dash-page__head { margin-bottom: 28px; }
.dash-page__h1 {
    font-family: var(--sc-font-display);
    font-size: clamp(28px, 4.2vw, 40px);
    font-weight: 380;
    line-height: 1.1;
    color: var(--sc-primary);
    margin: 4px 0 8px;
    font-variation-settings: "opsz" 96, "SOFT" 30;
}
.dash-meta {
    font-family: var(--sc-font-mono);
    font-size: 12px;
    color: var(--sc-moss);
    margin: 4px 0;
    letter-spacing: 0.04em;
}
.dash-section { margin: 36px 0; }

/* ---------------------------------------------------------------------------
   Wizard section grouping (UX pass 2026-06-24)

   The wizard steps stacked their sections with only whitespace (`.dash-section`
   margin), at uniform weight — a dense step (letter-template has ~10 sections)
   read as one long undifferentiated dump. Fix: give each section visual
   CONTAINMENT so related fields read as a group (Gestalt proximity + a real
   container), and demote "advanced/optional" sections so the eye lands on the
   essentials first.

   Scoped to `.dash-shell--wizard` so non-wizard dash pages (campaign detail,
   security, letter detail) keep the flat `.dash-section` look. The wizard form
   is itself a `.sc-card`; we neutralize that one big card into a transparent
   container so the inner sections become the cards (no nested-card busyness),
   matching the chosen "separate shaded cards" direction. The standalone
   document-upload section after the form is also a `.dash-section`, so it picks
   up the same card automatically. */
.dash-shell--wizard .wizard-form {
    background: transparent;
    border: 0;
    box-shadow: none;
    padding: 0;
}
.dash-shell--wizard .dash-section {
    margin: 0 0 16px;          /* between-card gap (was 36px 0) */
    padding: 22px 24px;        /* generous internal pad; within-card spacing < gap */
    background: var(--sc-surface);
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
    box-shadow: var(--sc-shadow-paper);
}
/* Quieter treatment for advanced / power-user sections: a recessed, dashed,
   shadowless card on the page ground so it reads as "secondary" next to the
   solid essential cards above it. Add `dash-section--advanced` in the template. */
.dash-shell--wizard .dash-section--advanced {
    background: transparent;
    border-style: dashed;
    box-shadow: none;
}
/* A section's head + meta should hug the fields they describe (tighter than the
   between-card gap), so the grouping is felt. */
.dash-shell--wizard .dash-section > .sc-card__head { margin-bottom: 6px; }
.dash-shell--wizard .dash-section > .dash-meta:first-of-type { margin-top: 0; }

/* ---------------------------------------------------------------------------
   Wizard meta + per-step intro typography (UX pass 2026-06-24)

   On dense steps the 12px `.dash-meta` lines under every section head stacked
   into a wall of fine print that competed with the fields for attention. Within
   the wizard we demote meta to a clear *caption* role so the reading order is
   head -> fields -> (only if needed) meta: smaller, lighter, calmer leading,
   slightly tighter tracking. We deliberately KEEP the `--sc-moss` colour rather
   than lightening it: moss on the card surface is ~5.5:1 (well above WCAG AA
   4.5:1), but nudging it lighter would risk dropping under AA on dense steps —
   the de-emphasis here is carried by size/weight/leading, not by fading colour.
   Scoped to `.dash-shell--wizard` so non-wizard dash pages keep 12px meta. */
.dash-shell--wizard .dash-meta {
    font-size: 11.5px;        /* caption, a notch under the 12px body meta */
    font-weight: 400;         /* lighter than surrounding labels/heads */
    line-height: 1.55;        /* calmer leading so multi-line meta breathes */
    letter-spacing: 0.02em;   /* tighter than the 0.04em global meta */
    /* colour stays var(--sc-moss) from the global rule — AA-safe (~5.5:1) */
}

/* Per-step orienting lead. The step templates open with
   <p class="dash-step-intro">…</p> directly under the step title — one calm
   sentence telling the author what this step is for. Body-sized (~14px) and
   muted-but-AA (moss, ~5.5:1 on surface) so it reads as a lead, not a heading
   and not fine print; sits between the H1 and the first section card. */
.dash-shell--wizard .dash-step-intro {
    font-family: var(--sc-font-mono);
    font-size: 14px;
    line-height: 1.5;
    color: var(--sc-moss);     /* muted, but AA-safe on the card/page ground */
    margin: 0 0 20px;          /* lead the eye down into the first section card */
    max-width: 64ch;           /* keep the one-liner from running edge-to-edge */
}
/* Inline "Required: …" hint the template may drop inside the intro. Render it as
   a small primary-tinted pill so the one thing the author MUST do on this step
   pops out of the calm lead without shouting. */
.dash-shell--wizard .dash-step-intro__required {
    display: inline-block;
    margin-left: 4px;
    padding: 1px 8px;
    border-radius: 999px;
    background: rgba(30, 58, 95, 0.10);  /* 10% tint of --sc-primary (#1E3A5F navy) */
    color: var(--sc-primary);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.dash-code {
    font-family: var(--sc-font-mono);
    background: rgba(14,79,77,0.06);
    color: var(--sc-primary);
    padding: 1px 6px;
    border-radius: 2px;
    font-size: 0.92em;
}

.dash-link {
    color: var(--sc-primary);
    text-decoration: none;
    border-bottom: 1px solid var(--sc-rule);
    transition: border-color 140ms ease, color 140ms ease;
}
.dash-link:hover { color: var(--sc-stamp); border-bottom-color: var(--sc-stamp); }

/* ---- Tables ------------------------------------------------------------- */
.dash-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--sc-panel);
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
    overflow: hidden;
    font-family: var(--sc-font-body);
    font-size: 14px;
}
.dash-table th, .dash-table td {
    text-align: left;
    padding: 10px 16px;
    border-bottom: 1px solid var(--sc-rule);
    vertical-align: middle;
}
.dash-table th {
    font-family: var(--sc-font-mono);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--sc-moss);
    background: rgba(14,79,77,0.04);
}
.dash-table tbody tr:last-child td { border-bottom: none; }
.dash-table tbody tr:hover { background: rgba(244,162,97,0.04); }
.dash-table__num { text-align: right; font-variant-numeric: tabular-nums; }
.dash-table__faint { color: var(--sc-moss); font-size: 13px; }
.dash-table__actions { text-align: right; white-space: nowrap; }

/* ---- Pills + tags ------------------------------------------------------- */
.dash-pill {
    display: inline-block;
    font-family: var(--sc-font-mono);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.16em;
    padding: 3px 7px;
    border-radius: 2px;
    background: rgba(14,79,77,0.08);
    color: var(--sc-primary);
}
.dash-pill--live    { background: rgba(60,130,60,0.16); color: #2c6a2c; }
.dash-pill--draft   { background: rgba(111,127,106,0.18); color: var(--sc-moss); }
.dash-pill--paused  { background: rgba(244,162,97,0.20); color: var(--sc-stamp); }
.dash-pill--archived{ background: rgba(26,26,26,0.10); color: #555; }
/* Letter-attempt status pills — chunk 4 of the letter-writer port. Same
   palette logic as the campaign status pills, mapped to the letter
   state machine in letters/moderation.py. */
.dash-pill--letter-draft                  { background: rgba(111,127,106,0.18); color: var(--sc-moss); }
.dash-pill--letter-pending_moderation     { background: rgba(244,162,97,0.20); color: var(--sc-stamp); }
.dash-pill--letter-auto_blocked_toxicity  { background: rgba(213,22,26,0.10); color: #b50d10; }
.dash-pill--letter-approved               { background: rgba(60,130,60,0.16); color: #2c6a2c; }
.dash-pill--letter-sent                   { background: rgba(14,79,77,0.14); color: var(--sc-primary); }
.dash-pill--letter-rejected               { background: rgba(26,26,26,0.10); color: #555; }
.dash-pill--admin   { background: rgba(213,22,26,0.10); color: #b50d10; }
.dash-pill--editor  { background: rgba(14,79,77,0.10); color: var(--sc-primary); }

.dash-pill--call-completed   { background: rgba(60,130,60,0.16); color: #2c6a2c; }
.dash-pill--call-queued      { background: rgba(111,127,106,0.16); color: var(--sc-moss); }
.dash-pill--call-ringing     { background: rgba(244,162,97,0.20); color: var(--sc-stamp); }
.dash-pill--call-in-progress { background: rgba(14,79,77,0.10); color: var(--sc-primary); }
.dash-pill--call-failed,
.dash-pill--call-busy,
.dash-pill--call-no-answer   { background: rgba(192,74,44,0.10); color: var(--sc-stamp); }

.dash-tag {
    font-family: var(--sc-font-mono);
    font-size: 10px;
    background: rgba(244,162,97,0.18);
    color: var(--sc-stamp);
    padding: 1px 5px;
    border-radius: 2px;
    margin-left: 6px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* ---- Stat grid ---------------------------------------------------------- */
.dash-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 0;
    border-top: 1px solid var(--sc-rule);
    border-bottom: 1px solid var(--sc-rule);
    margin: 24px 0 36px;
}
.dash-stat {
    padding: 18px 18px;
    border-right: 1px solid var(--sc-rule);
}
.dash-stat:last-child { border-right: none; }
.dash-stat__num {
    font-family: var(--sc-font-mono);
    font-size: 26px;
    font-weight: 700;
    color: var(--sc-primary);
    line-height: 1;
    letter-spacing: -0.02em;
    margin-bottom: 6px;
    font-variant-numeric: tabular-nums;
}
.dash-stat__label {
    font-family: var(--sc-font-mono);
    font-size: 10px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--sc-moss);
}

/* ---- Login + forms ------------------------------------------------------ */
.dash-login {
    max-width: 480px;
    margin: 32px auto 0;
}
.dash-login__form { margin-top: 32px; }

.dash-filter-row {
    display: flex;
    gap: 10px;
    margin-bottom: 22px;
    align-items: center;
}

/* ---- Toolbar + filter chips (home page) ---------------------------------
   Compact horizontal row that pairs a primary action (New campaign) with
   a chip-style segmented filter (Active / Archived / All). Chips read
   as a single rounded group; the active one inverts. */
.dash-toolbar {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}
.dash-filter-chips {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--sc-rule);
    border-radius: 999px;
    overflow: hidden;
    font-family: var(--sc-font-mono);
    font-size: 12px;
    letter-spacing: 0.05em;
}
.dash-chip {
    padding: 6px 14px;
    color: var(--sc-moss);
    text-decoration: none;
    transition: background-color 140ms ease, color 140ms ease;
    border-right: 1px solid var(--sc-rule);
}
.dash-chip:last-child { border-right: none; }
.dash-chip:hover { background: rgba(0, 0, 0, 0.04); color: var(--sc-ink); }
.dash-chip--active {
    background: var(--sc-ink);
    color: var(--sc-paper);
}
.dash-chip--active:hover {
    background: var(--sc-ink);
    color: var(--sc-paper);
}

/* ---- Archived campaign row treatment ----------------------------------- */
.dash-table__row--archived td {
    color: var(--sc-moss);
    opacity: 0.7;
}
.dash-table__row--archived .dash-code {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--sc-rule);
}

/* ---- Secondary action row (campaign detail — archive, etc) -------------
   Keeps destructive actions visually separated from the read-only links
   above it. Forms in this row are inline so the button sits where you'd
   expect a button, not on its own block. */
.dash-page__actions--secondary {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--sc-rule);
}
.dash-page__actions--secondary form {
    display: inline-flex;
    margin: 0;
}

/* ---- TOTP setup --------------------------------------------------------- */
.dash-totp-qr {
    background: var(--sc-surface);
    padding: 16px;
    display: inline-block;
    border: 1px solid var(--sc-rule);
    margin: 14px 0;
}
.dash-totp-qr svg { width: 200px; height: 200px; }

.dash-pagination { margin-top: 18px; }

/* ---- Wizard stepper (T1.9b) -------------------------------------------- */
.wizard-stepper {
    margin: 22px 0 36px;
    border-top: 1px solid var(--sc-rule);
    border-bottom: 1px solid var(--sc-rule);
}
.wizard-stepper__list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    list-style: none;
    margin: 0;
    padding: 0;
}
.wizard-stepper__item {
    border-right: 1px solid var(--sc-rule);
}
.wizard-stepper__item:last-child { border-right: none; }
.wizard-stepper__link {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: 16px 18px;
    text-decoration: none;
    color: var(--sc-moss);
    font-family: var(--sc-font-mono);
    border-bottom: 3px solid transparent;
    transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
.wizard-stepper__link:hover { color: var(--sc-primary); }
.wizard-stepper__item--active .wizard-stepper__link {
    color: var(--sc-primary);
    border-bottom-color: var(--sc-accent);
    background: rgba(244,162,97,0.05);
}
.wizard-stepper__item--done .wizard-stepper__link {
    color: var(--sc-primary);
    border-bottom-color: var(--sc-rule);
}
.wizard-stepper__num {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.18em;
    margin-bottom: 4px;
}
.wizard-stepper__label {
    font-family: var(--sc-font-display);
    font-size: 18px;
    color: var(--sc-primary);
}

.wizard-pager {
    margin: 40px 0 0;
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    align-items: center;   /* so the larger primary aligns with the ghost Back btn */
}

/* Make the step's primary action (Save / Next) visually THE next step (UX pass
   2026-06-24). The base .sc-btn is already 16px/600 with 16px vertical pad
   (~48px tall), but in the pager it sat at the same weight as the secondary
   Back/ghost buttons. Bump only the pager's primary so it clearly dominates:
   bigger type + heavier weight + more horizontal reach, while staying within
   the design system (no new colour/font — same gradient + --sc-shadow-btn as
   the supporter-side primary). Vertical padding (18px*2 + ~19px line) keeps it
   well above the 44px touch-target minimum. Scoped to the wizard pager so the
   global .sc-btn--primary used elsewhere is untouched. */
.dash-shell--wizard .wizard-pager .sc-btn--primary {
    font-size: 17px;
    font-weight: 700;
    padding: 18px 32px;        /* taller + wider than the default 16px/26px */
    letter-spacing: 0.01em;
}

/* ---------------------------------------------------------------------------
   Wizard live-preview layout (preview_pane.html.j2)

   The wizard steps render <main class="dash-shell dash-shell--wizard">. We turn
   that <main> into a two-column grid: the step content stacks in column 1, the
   live-preview <aside> pins to column 2. Every direct child EXCEPT the preview
   is forced into column 1, so we don't have to wrap each (varied) step body in
   a container — the grid does the splitting. The preview spans all rows and is
   `position: sticky` so it stays in view while the author scrolls a long form.
   ------------------------------------------------------------------------- */
.dash-shell--wizard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 400px;
    /* column-gap only. A row-gap here is catastrophic: the preview spans
       `grid-row: 1 / 9999` (so its sticky cell is as tall as the form column),
       which forces ~9998 row tracks to exist — a row-gap would then add 28px
       between every one of them (~280,000px of phantom page height). Row
       spacing between the stacked step blocks is supplied by margin-bottom
       below instead. */
    column-gap: 28px;
    row-gap: 0;
    align-items: start;
    max-width: 1400px;  /* wider than the 1180 single-column shell to fit both */
}
/* Keep all step content (header, stepper, forms, banners) in the first column,
   stacked, each carrying its own bottom margin (row-gap is 0 — see above). */
.dash-shell--wizard > :not(.wizard-preview) {
    grid-column: 1;
    min-width: 0;  /* let long content shrink instead of blowing out the grid */
    margin-bottom: 28px;
}
.wizard-preview {
    grid-column: 2;
    grid-row: 1 / 9999;   /* span the full height of the content column */
    position: sticky;
    top: 24px;
    align-self: start;
    display: block;
}
.wizard-preview__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}
.wizard-preview__tools {
    display: flex;
    align-items: center;
    gap: 10px;
}
/* Device-width segmented control (Desktop / Tablet / Mobile). Mono micro-caps
   to match the dash's stepper + pill vocabulary. */
.wizard-preview__devices {
    display: inline-flex;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-sm);
    overflow: hidden;
    background: var(--sc-paper);
}
.wizard-preview__device {
    appearance: none;
    border: 0;
    border-left: 1px solid var(--sc-rule);
    background: transparent;
    color: var(--sc-moss);
    font-family: var(--sc-font-mono);
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 5px 10px;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.wizard-preview__device:first-child { border-left: 0; }
.wizard-preview__device:hover { color: var(--sc-primary); }
.wizard-preview__device.is-active { background: var(--sc-primary); color: #fff; }
.wizard-preview__refresh {
    font-size: 12px;
    padding: 6px 12px;
}
.wizard-preview__width {
    font-family: var(--sc-font-mono);
    font-weight: 700;
    color: var(--sc-primary);
}

/* Stage wraps the viewport; at a non-desktop width it gets a soft "device
   gutter" tint so the scaled frame reads as a device, not a broken page. */
.wizard-preview__stage {
    border-radius: var(--sc-radius-sm);
    transition: background 0.15s ease, padding 0.15s ease;
}
.wizard-preview__stage[data-device="tablet"],
.wizard-preview__stage[data-device="mobile"] {
    background: rgba(111, 127, 106, 0.10);
    padding: 12px;
}
/* The viewport is the fixed-size window the scaled frame is clipped to.
   Viewport-relative height so tall supporter pages aren't clipped into a tiny
   nested scrollbar, and short pages don't leave a 720px void. The sticky pane
   keeps it in view as the form scrolls. */
.wizard-preview__viewport {
    position: relative;
    width: 100%;
    height: calc(100vh - 168px);
    min-height: 460px;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-sm);
    overflow: hidden;
    background: var(--sc-panel);
}
/* The scaler is transform-scaled (top-left origin) by the script to fit the
   real-device-width iframe into the viewport; `left` is set inline to centre
   the scaled frame. */
.wizard-preview__scaler {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: top left;
    will-change: transform;
}
/* Load / error overlay over the frame — hides the white flash of an src reload
   and stops a blank/lazy frame from reading as "the page is empty." */
.wizard-preview__overlay {
    position: absolute;
    inset: 0;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    background: var(--sc-panel);
    color: var(--sc-moss);
    font-family: var(--sc-font-mono);
    font-size: 12px;
    letter-spacing: 0.04em;
    z-index: 2;
}
.wizard-preview__overlay.is-loading,
.wizard-preview__overlay.is-error { display: flex; }
.wizard-preview__overlay.is-error { color: var(--sc-stamp); }
.wizard-preview__spinner {
    width: 22px;
    height: 22px;
    border: 2px solid var(--sc-rule);
    border-top-color: var(--sc-primary);
    border-radius: 50%;
    animation: wizardPreviewSpin 0.7s linear infinite;
}
.wizard-preview__overlay.is-error .wizard-preview__spinner { display: none; }
@keyframes wizardPreviewSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .wizard-preview__spinner { animation: none; }
}

/* On narrow viewports the side-by-side grid is cramped; collapse to a single
   column with the preview LAST (after the form) and let it scroll with the
   page rather than stay sticky. */
@media (max-width: 1024px) {
    .dash-shell--wizard {
        grid-template-columns: 1fr;
        max-width: 1180px;
    }
    .dash-shell--wizard > :not(.wizard-preview),
    .wizard-preview {
        grid-column: 1;
    }
    .wizard-preview {
        grid-row: auto;
        position: static;
        margin-top: 28px;
    }
}

.wizard-explainer {
    background: var(--sc-panel);
    border: 1px dashed var(--sc-rule);
    border-radius: var(--sc-radius-md);
    padding: 16px 20px;
    font-size: 14px;
    line-height: 1.55;
    color: var(--sc-ink);
}

.wizard-collapse {
    margin: 22px 0;
    background: var(--sc-panel);
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
}
.wizard-collapse > summary {
    cursor: pointer;
    padding: 14px 18px;
    font-family: var(--sc-font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--sc-primary);
}
.wizard-collapse > summary:hover { background: rgba(244,162,97,0.06); }
.wizard-collapse[open] > summary { border-bottom: 1px solid var(--sc-rule); }
.wizard-form {
    margin: 0;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
    padding: 18px;
}
.wizard-form::before { display: none; }
.wizard-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px 22px;
    margin-bottom: 14px;
}

.dash-link--danger {
    color: var(--sc-stamp);
    background: none;
    border: none;
    border-bottom: 1px solid rgba(192,74,44,0.25);
    font-family: var(--sc-font-body);
    font-size: 14px;
    cursor: pointer;
    padding: 0;
}
.dash-link--danger:hover { border-bottom-color: var(--sc-stamp); }

/* ---- Launch checklist (T1.9b Review step) ----------------------------- */
.dash-checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
    background: var(--sc-panel);
}
.dash-checklist__row {
    display: grid;
    grid-template-columns: 44px 1fr;
    gap: 14px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--sc-rule);
    align-items: start;
}
.dash-checklist__row:last-child { border-bottom: none; }
.dash-checklist__mark {
    font-family: var(--sc-font-mono);
    font-weight: 700;
    font-size: 18px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.dash-checklist__row--ok .dash-checklist__mark {
    background: rgba(60,130,60,0.18);
    color: #2c6a2c;
}
.dash-checklist__row--fail .dash-checklist__mark {
    background: rgba(192,74,44,0.16);
    color: var(--sc-stamp);
}
.dash-checklist__label {
    font-family: var(--sc-font-mono);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--sc-primary);
    margin: 0 0 2px;
}
.dash-checklist__detail {
    font-family: var(--sc-font-body);
    font-size: 14px;
    color: var(--sc-moss);
    margin: 0;
}

/* -----------------------------------------------------------------------
 * Recipient-preset picker (wizard step 1, T1.9b extension)
 * Editorial card grid grouped by tier — Primary / Regional / By party / Role.
 * Selected card gets a left rule (pull-quote) instead of a fill; descriptions
 * fade in on hover so the grid stays scannable.
 * -------------------------------------------------------------------- */

.preset-block {
    margin: 0 0 28px;
    padding: 22px 22px 24px;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-lg);
    background: var(--sc-surface);
    position: relative;
}
.preset-block__kicker {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--sc-primary);
    margin: 0 0 6px;
}
.preset-block__h2 {
    font-family: var(--sc-font-display);
    font-weight: 500;
    font-size: 24px;
    line-height: 1.15;
    margin: 0 0 4px;
    color: var(--sc-ink);
    letter-spacing: -0.01em;
}
.preset-block__deck {
    font-family: var(--sc-font-body);
    font-size: 14px;
    line-height: 1.5;
    color: var(--sc-moss);
    margin: 0 0 18px;
    max-width: 58ch;
}

/* Top-level jurisdiction grouping (Federal / Provincial). Wraps the per-tier
   groups so the federal "By party"/"By role" rows can't be misread as applying
   to the provincial set. A <fieldset> for semantics + keyboard/AT grouping. */
.preset-juris {
    border: 1px solid var(--sc-rule);
    border-radius: 12px;
    padding: 8px 18px 18px;
    margin: 20px 0;
}
.preset-juris__legend {
    font-family: var(--sc-font-mono);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--sc-ink);
    padding: 2px 10px;
}
/* First tier inside a jurisdiction box shouldn't double the top margin. */
.preset-juris .preset-tier:first-of-type {
    margin-top: 8px;
}

.preset-tier {
    margin: 22px 0 6px;
}
.preset-tier__label {
    font-family: var(--sc-font-mono);
    font-size: 10px;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--sc-moss);
    margin: 0 0 10px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.preset-tier__label::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--sc-rule);
}

.preset-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 12px;
}
.preset-card {
    position: relative;
    padding: 14px 16px 14px 18px;
    border: 1px solid var(--sc-rule);
    border-left: 3px solid transparent;
    border-radius: var(--sc-radius-md);
    background: var(--sc-paper);
    cursor: pointer;
    transition: border-color 140ms ease, transform 140ms ease, background 140ms ease;
}
.preset-card:hover {
    border-color: rgba(37, 99, 235, 0.35);
    transform: translateY(-1px);
}
/* Selector covers both the old radio markup (kept for any not-yet-
   migrated callers) and the multi-select checkbox introduced
   2026-05-27. Both visually hide the input — the card border is
   the affordance. */
.preset-card__radio,
.preset-card__check {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.preset-card__title {
    font-family: var(--sc-font-display);
    font-weight: 500;
    font-size: 16px;
    line-height: 1.25;
    color: var(--sc-ink);
    margin: 0;
    letter-spacing: -0.005em;
}
.preset-card__desc {
    font-family: var(--sc-font-body);
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--sc-moss);
    margin: 6px 0 0;
    /* clamp on hover-fade so the grid stays neat at rest */
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    transition: max-height 220ms ease, opacity 200ms ease 40ms;
}
.preset-card:hover .preset-card__desc,
.preset-card.is-selected .preset-card__desc,
.preset-card:focus-within .preset-card__desc {
    max-height: 7em;
    opacity: 1;
}
.preset-card__radio:checked + .preset-card__title + .preset-card__desc,
.preset-card__check:checked + .preset-card__title + .preset-card__desc,
.preset-card.is-selected {
    /* match — see :has() fallback below */
}
.preset-card:has(.preset-card__radio:checked),
.preset-card:has(.preset-card__check:checked),
.preset-card.is-selected {
    border-left-color: var(--sc-accent);
    background: linear-gradient(
        90deg,
        rgba(37, 99, 235, 0.04) 0%,
        rgba(37, 99, 235, 0) 50%
    );
}
.preset-card:has(.preset-card__radio:checked) .preset-card__desc,
.preset-card:has(.preset-card__check:checked) .preset-card__desc {
    max-height: 7em;
    opacity: 1;
}

.preset-actions {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 18px 0 0;
    flex-wrap: wrap;
}
.preset-actions__hint {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    color: var(--sc-moss);
    letter-spacing: 0.04em;
}

.preset-confirm {
    display: none;
    margin: 14px 0 0;
    padding: 12px 14px;
    border: 1px dashed rgba(220, 38, 38, 0.45);
    border-radius: var(--sc-radius-md);
    background: rgba(254, 226, 226, 0.45);
    color: #7f1d1d;
    font-family: var(--sc-font-body);
    font-size: 13px;
    line-height: 1.4;
}
.preset-confirm.is-shown { display: block; animation: preset-slide-in 180ms ease; }
@keyframes preset-slide-in {
    from { transform: translateY(-4px); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}
.preset-confirm strong { font-weight: 600; }
.preset-confirm__row {
    display: flex;
    gap: 10px;
    margin: 10px 0 0;
}

.preset-result {
    margin: 14px 0 0;
}

/* Fallback target picker — pull-quote treatment matching the preset block. */
.fallback-block {
    margin: 0 0 28px;
    padding: 22px 22px 24px;
    border: 1px solid var(--sc-rule);
    border-left: 3px solid var(--sc-primary);
    border-radius: var(--sc-radius-lg);
    background: var(--sc-surface);
}
.fallback-block__kicker {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--sc-primary);
    margin: 0 0 6px;
}
.fallback-block__h2 {
    font-family: var(--sc-font-display);
    font-weight: 500;
    font-size: 22px;
    line-height: 1.2;
    margin: 0 0 4px;
    color: var(--sc-ink);
    letter-spacing: -0.005em;
}
.fallback-block__deck {
    font-family: var(--sc-font-body);
    font-size: 14px;
    line-height: 1.5;
    color: var(--sc-moss);
    margin: 0 0 14px;
    max-width: 58ch;
}
.fallback-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.fallback-row select {
    min-width: 280px;
    flex: 1;
}

/* -----------------------------------------------------------------------
 * WYSIWYG editor (Trix) — Message step
 * Wrap Trix's default chrome to match the dash editorial palette.
 * -------------------------------------------------------------------- */
.wysiwyg-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 0 0 8px;
    flex-wrap: wrap;
}
.wysiwyg-toolbar__hint {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    color: var(--sc-moss);
    letter-spacing: 0.06em;
}
.wysiwyg-toolbar__toggle {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--sc-primary);
    background: transparent;
    border: 1px solid var(--sc-rule);
    border-radius: 999px;
    padding: 4px 12px;
    cursor: pointer;
    transition: border-color 140ms ease, color 140ms ease;
}
.wysiwyg-toolbar__toggle:hover {
    border-color: var(--sc-accent);
    color: var(--sc-accent);
}

trix-toolbar {
    border-bottom: 1px solid var(--sc-rule);
    padding: 6px 6px 8px;
    background: var(--sc-paper);
    border-radius: var(--sc-radius-md) var(--sc-radius-md) 0 0;
}
trix-toolbar .trix-button-group {
    border-color: var(--sc-rule) !important;
    border-radius: var(--sc-radius-md);
    background: var(--sc-surface);
    margin-right: 6px;
}
trix-toolbar .trix-button {
    border-color: transparent !important;
    color: var(--sc-ink);
}
trix-toolbar .trix-button:not(:disabled):hover {
    background: rgba(37, 99, 235, 0.08);
}
trix-toolbar .trix-button.trix-active {
    background: rgba(37, 99, 235, 0.15);
    color: var(--sc-accent);
}

trix-editor {
    min-height: 220px;
    border: 1px solid var(--sc-rule);
    border-top: none;
    border-radius: 0 0 var(--sc-radius-md) var(--sc-radius-md);
    background: var(--sc-surface);
    padding: 14px 16px;
    font-family: var(--sc-font-body);
    font-size: 15px;
    line-height: 1.55;
    color: var(--sc-ink);
}
trix-editor:focus {
    outline: 2px solid rgba(37, 99, 235, 0.35);
    outline-offset: -2px;
}
trix-editor h1, trix-editor h2, trix-editor h3 {
    font-family: var(--sc-font-display);
    font-weight: 500;
    letter-spacing: -0.005em;
    margin-top: 0.6em;
}
trix-editor a {
    color: var(--sc-accent);
}

/* -----------------------------------------------------------------------
 * Hero image upload — Design step
 * Editorial-chrome dropzone: thin rule border, typographic lede instead
 * of an icon, accent ribbon on hover/drag. Matches the wysiwyg-toolbar
 * pattern (mono-uppercase mode pills, soft radii, 140ms transitions).
 * -------------------------------------------------------------------- */
.hero-upload {
    /* The component lives inside .sc-field so it inherits the field
       spacing rhythm; .hero-upload just adds its own internal layout. */
}
.hero-upload__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin: 0 0 10px;
    flex-wrap: wrap;
}
.hero-upload__modes {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.hero-upload__mode {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--sc-moss);
    background: transparent;
    border: none;
    padding: 4px 2px;
    cursor: pointer;
    border-bottom: 1px solid transparent;
    transition: color 140ms ease, border-color 140ms ease;
}
.hero-upload__mode:hover { color: var(--sc-primary); }
.hero-upload__mode.is-active {
    color: var(--sc-primary);
    border-bottom-color: var(--sc-accent);
}
.hero-upload__mode-sep {
    color: var(--sc-rule);
    font-family: var(--sc-font-mono);
    font-size: 11px;
}

/* ---- Dropzone ---- */
.hero-upload__form { margin: 0; }
.hero-upload__dropzone {
    display: block;
    position: relative;
    border: 1px dashed var(--sc-rule);
    border-radius: var(--sc-radius-md);
    background: var(--sc-surface, transparent);
    padding: 38px 28px;
    text-align: center;
    cursor: pointer;
    transition: border-color 160ms ease, background-color 160ms ease,
                box-shadow 160ms ease;
}
.hero-upload__dropzone:hover,
.hero-upload__dropzone:focus-visible {
    border-color: var(--sc-accent);
    background: rgba(37, 99, 235, 0.04);
    outline: none;
}
.hero-upload__dropzone.is-dragover {
    border-color: var(--sc-accent);
    border-style: solid;
    background: rgba(37, 99, 235, 0.08);
    box-shadow: inset 0 0 0 1px var(--sc-accent);
}
.hero-upload__dropzone.is-staged {
    cursor: default;
    border-style: solid;
    background: var(--sc-surface, transparent);
}
.hero-upload__dropzone-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}
.hero-upload__rule {
    display: block;
    width: 44px;
    height: 1px;
    background: var(--sc-rule);
    margin-bottom: 6px;
}
.hero-upload__lede {
    font-family: var(--sc-font-display, var(--sc-font-body));
    font-size: 18px;
    color: var(--sc-ink);
    margin: 0;
    letter-spacing: -0.005em;
}
.hero-upload__hint {
    font-family: var(--sc-font-body);
    font-size: 14px;
    color: var(--sc-moss);
    margin: 0;
}
.hero-upload__pickword {
    color: var(--sc-accent);
    border-bottom: 1px solid var(--sc-accent);
    padding-bottom: 1px;
}
.hero-upload__specs {
    margin-top: 4px;
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* ---- Staged preview (before submit) ----
   Lives outside the <label> dropzone (so its action buttons don't reopen
   the file picker on click — see the comment in design.html.j2). Gets
   roughly the same padded box treatment as the dropzone so the visual
   transition feels continuous. */
.hero-upload__staged {
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
    background: var(--sc-surface, transparent);
    padding: 38px 28px;
}
.hero-upload__staged-thumb {
    max-width: 280px;
    max-height: 160px;
    width: auto;
    height: auto;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
    object-fit: cover;
    margin-bottom: 4px;
    /* Tiny entrance — the picked thumbnail fades + scales in so the
       transition from empty to staged isn't an abrupt swap. */
    animation: hero-upload-staged-in 220ms ease-out;
}
@keyframes hero-upload-staged-in {
    from { opacity: 0; transform: scale(0.96); }
    to   { opacity: 1; transform: scale(1); }
}

/* ---- Progress bar (HTMX indicator) ---- */
.hero-upload__progress {
    margin-top: 10px;
    display: none;
    align-items: center;
    gap: 10px;
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--sc-moss);
}
.hero-upload__progress.htmx-request,
.htmx-request .hero-upload__progress {
    display: flex;
}
.hero-upload__progress-bar {
    flex: 1;
    height: 2px;
    background: var(--sc-rule);
    border-radius: 2px;
    position: relative;
    overflow: hidden;
}
.hero-upload__progress-bar::after {
    content: "";
    position: absolute;
    top: 0; left: 0;
    height: 100%;
    width: 40%;
    background: var(--sc-accent);
    animation: hero-upload-progress 1100ms ease-in-out infinite;
}
@keyframes hero-upload-progress {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(260%); }
}

/* ---- Post-upload preview (after HTMX swap) ---- */
.hero-upload__result { margin-top: 10px; }
.hero-upload__preview {
    display: grid;
    grid-template-columns: 240px 1fr;
    gap: 18px;
    align-items: start;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
    padding: 14px;
    background: var(--sc-surface, transparent);
    /* Slide-up entrance, swapping the dropzone for the preview. */
    animation: hero-upload-result-in 280ms ease-out;
}
@keyframes hero-upload-result-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}
.hero-upload__thumb {
    width: 100%;
    height: auto;
    max-height: 140px;
    object-fit: cover;
    border: 1px solid var(--sc-rule);
    border-radius: var(--sc-radius-md);
}
.hero-upload__meta { min-width: 0; }
.hero-upload__caption {
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--sc-primary);
    margin: 0 0 6px;
}
.hero-upload__actions {
    margin-top: 10px;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
}

@media (max-width: 720px) {
    .hero-upload__preview { grid-template-columns: 1fr; }
    .hero-upload__staged-thumb { max-width: 100%; }
}

/* ---------------------------------------------------------------------
 * Letter-writer port chunk 6 — kind picker + kind chip + letter-template
 * surface. Class prefixes (.kind-, .letter-tpl-) are unique to this chunk
 * so they don't collide with anything else in dash.css. JetBrains Mono
 * everywhere via inherited --sc-font-* tokens.
 * --------------------------------------------------------------------- */

/* Two-card radio picker on /dash/campaigns/new — visually similar to the
   preset-card grid but operates as a binary choice (call vs letter).
   Hidden native radios; the card border is the affordance. */
.sc-field--kind-picker {
    border: 0;
    padding: 0;
    margin: 0 0 22px;
}
.kind-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
    margin: 10px 0;
}
.kind-card {
    position: relative;
    padding: 16px 18px;
    border: 1px solid var(--sc-rule);
    border-left: 3px solid transparent;
    border-radius: var(--sc-radius-md);
    background: var(--sc-paper);
    cursor: pointer;
    transition: border-color 140ms ease, transform 140ms ease;
}
.kind-card:hover { border-color: rgba(37, 99, 235, 0.35); transform: translateY(-1px); }
.kind-card input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.kind-card__title {
    font-family: var(--sc-font-display);
    font-weight: 500;
    font-size: 16px;
    line-height: 1.25;
    color: var(--sc-ink);
    margin: 0;
}
.kind-card__desc {
    font-family: var(--sc-font-body);
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--sc-moss);
    margin: 6px 0 0;
}
.kind-card:has(input[type="radio"]:checked) {
    border-left-color: var(--sc-accent);
    background: linear-gradient(90deg, rgba(37, 99, 235, 0.05) 0%, rgba(37, 99, 235, 0) 55%);
}

/* Kind chip shown on the campaign detail page next to the status pill.
   Two muted color variants so call vs letter is visually distinct
   without competing with the (more important) status pill colour. */
.dash-kind-chip {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-family: var(--sc-font-mono);
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    border: 1px solid var(--sc-rule);
    color: var(--sc-moss);
    background: var(--sc-paper);
    vertical-align: middle;
}
.dash-kind-chip--call   { border-color: rgba(37, 99, 235, 0.3); color: #1d4ed8; }
.dash-kind-chip--letter { border-color: rgba(124, 58, 237, 0.3); color: #6d28d9; }

/* Letter template step — a single full-width card with sectioned fields.
   Re-uses .sc-card, .sc-field, etc. Only the email-prominence styling on
   the targets table is unique. */
.targets-table--letter th.col-email,
.targets-table--letter td.col-email {
    /* Make the email column 100% of the visible width it can claim. The
       phone column stays available (hybrid campaigns) but loses
       emphasis. */
    font-family: var(--sc-font-mono);
    font-size: 12.5px;
    color: var(--sc-ink);
    word-break: break-word;
}
.targets-table--letter td.col-phone-faint {
    color: var(--sc-moss);
    font-size: 12px;
}

@media (max-width: 720px) {
    .dash-nav__inner { font-size: 10px; padding: 0 14px; gap: 0; overflow-x: auto; }
    .dash-nav__link { padding: 12px 10px; }
    .dash-table { font-size: 13px; }
    .dash-table th, .dash-table td { padding: 9px 10px; }
    .dash-stat__num { font-size: 22px; }
    .wizard-stepper__list { grid-template-columns: repeat(2, 1fr); }
    .wizard-grid { grid-template-columns: 1fr; }
    .preset-grid { grid-template-columns: 1fr; }
    .kind-grid { grid-template-columns: 1fr; }
}

/* Operator banner — drafting-disabled warning (issue #1). Amber, full-width,
   sits between the nav and the page content. Self-contained so it doesn't
   depend on a warn token the palette doesn't define. */
.dash-banner {
    max-width: 1100px;
    margin: 12px auto 0;
    padding: 12px 16px;
    border-radius: 8px;
    font-size: 0.92rem;
    line-height: 1.45;
}
.dash-banner--warn {
    background: #FEF3C7;       /* amber-100 */
    color: #7C4A03;            /* amber-900 ink */
    border: 1px solid #F59E0B; /* amber-500 */
}
.dash-banner code {
    background: rgba(124, 74, 3, 0.12);
    padding: 0 4px;
    border-radius: 4px;
}
[data-theme="dark"] .dash-banner--warn {
    background: #3A2A06;
    color: #FBD38D;
    border-color: #B7791F;
}
[data-theme="dark"] .dash-banner code {
    background: rgba(251, 211, 141, 0.14);
}
