/* ============================================================
   Design system — shared FT canonical tokens (from ft-configs-ui).
   Steel-blue brand (primary #2b6eac, accent #1f4e7a), blue-tinted
   neutrals, status bg/text/border triplets. Typeface: Geist (Sans +
   Mono), self-hosted. Tokens are HSL triplets read via hsl(var(--x))
   for fidelity with the source system; cicd semantic aliases map onto
   them so existing call sites keep working. WCAG 2.1 AA in both
   themes. The purple gradient is removed (named anti-reference).
   ============================================================ */

@font-face { font-family: 'Geist'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/geist-sans-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family: 'Geist'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/geist-sans-latin-500-normal.woff2') format('woff2'); }
@font-face { font-family: 'Geist'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/geist-sans-latin-600-normal.woff2') format('woff2'); }
@font-face { font-family: 'Geist'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/geist-sans-latin-700-normal.woff2') format('woff2'); }
@font-face { font-family: 'Geist Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/geist-mono-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family: 'Geist Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/geist-mono-latin-500-normal.woff2') format('woff2'); }

/* Light theme */
:root {
    /* --- Canonical FT tokens (HSL triplets) --- */
    --background: 0 0% 100%;
    --foreground: 0 0% 16.9%;
    --card: 0 0% 100%;
    --primary: 208.8 60% 42.2%;        /* #2b6eac */
    --primary-hover: 209 60% 36%;       /* #255e92 */
    --secondary: 209 60.8% 90%;        /* #d6e6f5 light blue */
    --secondary-strong: 209 55% 82%;   /* ~#b5cce2 */
    --muted: 210 20% 94%;
    --muted-foreground: 0 0% 40%;      /* #666; AA on white */
    --accent: 209 59.5% 30%;           /* #1f4e7a deep blue */
    --border: 210 16% 84%;
    --ring: 208.8 60% 42.2%;
    --page-chrome: 210 28% 95%;        /* ~#edf1f6 canvas, tinted toward brand */
    --radius: 8px;
    --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
    --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

    /* status badge triplets (bg / text / border) */
    --status-success-bg: 145 63% 95%;
    --status-success-text: 149 46% 30%;
    --status-success-border: 148 45% 85%;
    --status-warning-bg: 40 96% 94%;
    --status-warning-text: 32 81% 33%;
    --status-warning-border: 40 70% 84%;
    --status-error-bg: 350 100% 96%;
    --status-error-text: 350 70% 38%;
    --status-error-border: 350 65% 88%;
    --status-info-bg: 209 61% 94%;
    --status-info-text: 209 60% 32%;
    --status-info-border: 209 45% 84%;

    /* --- cicd semantic aliases --- */
    --bg-primary: hsl(var(--page-chrome));   /* page canvas + recessed input fill */
    --bg-secondary: hsl(var(--card));        /* cards, modals, panels */
    --surface-sunken: hsl(var(--muted));     /* passive panels, info boxes, table headers */
    --surface-interactive: hsl(var(--secondary));        /* secondary buttons, tabs — reads clickable */
    --surface-interactive-hover: hsl(var(--secondary-strong));
    --bg-tertiary: hsl(var(--muted));

    --text-primary: hsl(var(--foreground));
    --text-secondary: hsl(0 0% 32%);
    --text-muted: hsl(var(--muted-foreground));
    --border-color: hsl(var(--border));
    --border-strong: hsl(0 0% 70%);

    --accent-primary: hsl(var(--primary));        /* solid primary fill; white text 5.35:1 */
    --accent-primary-hover: hsl(var(--primary-hover));
    --accent-text: hsl(var(--accent));            /* accent text/icon/border (8.5:1 on white) */
    --accent-tint: hsl(var(--secondary));         /* active tab / current selection */
    --accent-tint-strong: hsl(var(--secondary-strong));
    --accent-ring: hsl(var(--ring) / 0.40);

    /* semantic fills (white/ink text safe) + tints */
    --success: hsl(149 50% 30%);
    --success-bg: hsl(var(--status-success-bg));
    --warning: hsl(35 92% 45%);
    --warning-bg: hsl(var(--status-warning-bg));
    --danger: hsl(4 72% 47%);
    --danger-hover: hsl(4 72% 40%);
    --danger-bg: hsl(var(--status-error-bg));
    --info: hsl(var(--accent));

    /* elevation — layered, brand-tinted depth. Three tiers: resting card,
       raised (interactive/hover), and overlay (modal/popover). */
    --shadow: 0 1px 2px hsl(208 28% 26% / 0.05), 0 3px 8px hsl(208 28% 26% / 0.07);
    --shadow-md: 0 2px 4px hsl(208 28% 24% / 0.06), 0 6px 16px hsl(208 30% 24% / 0.10);
    --shadow-hover: 0 10px 30px hsl(208 34% 22% / 0.16);

    --radius-lg: 14px;
    /* Motion (emil): color/background hovers use a quick ease; transforms and
       entrances use the stronger ease-out curve. All UI motion stays <300ms. */
    --transition: 0.18s ease;
    --press: 0.12s var(--ease-out);   /* :active press feedback */
    --focus-ring: 0 0 0 3px var(--accent-ring);

    --font-sans: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --font-display: var(--font-sans);
    --font-mono: 'Geist Mono', 'SFMono-Regular', 'Menlo', monospace;
}

/* Dark theme */
[data-theme="dark"] {
    --background: 0 0% 16.9%;
    --foreground: 0 1.7% 88.4%;
    --card: 0 0% 20%;
    --primary: 208.8 60% 46%;          /* a touch brighter on dark */
    --primary-hover: 208.8 62% 54%;
    --secondary: 208.7 60.5% 16%;      /* deep blue */
    --secondary-strong: 208.7 55% 22%;
    --muted: 0 0% 16.1%;
    --muted-foreground: 0 0% 62%;
    --accent: 209 70% 66%;             /* lighter blue for text on dark */
    --border: 0 0% 26%;
    --ring: 208.8 70% 60%;
    --page-chrome: 210 14% 11%;        /* tinted dark canvas */

    --status-success-bg: 150 35% 20%;
    --status-success-text: 145 55% 75%;
    --status-success-border: 149 28% 30%;
    --status-warning-bg: 35 50% 18%;
    --status-warning-text: 40 80% 72%;
    --status-warning-border: 38 38% 28%;
    --status-error-bg: 350 45% 20%;
    --status-error-text: 350 75% 78%;
    --status-error-border: 350 38% 30%;
    --status-info-bg: 209 45% 18%;
    --status-info-text: 209 65% 75%;
    --status-info-border: 209 38% 28%;

    --text-secondary: hsl(0 0% 70%);
    --border-strong: hsl(0 0% 38%);

    --success: hsl(149 45% 42%);
    --warning: hsl(38 80% 50%);
    --danger: hsl(4 75% 55%);
    --danger-hover: hsl(4 75% 48%);

    --shadow: 0 1px 2px hsl(0 0% 0% / 0.35), 0 3px 8px hsl(0 0% 0% / 0.30);
    --shadow-md: 0 2px 4px hsl(0 0% 0% / 0.35), 0 6px 16px hsl(0 0% 0% / 0.40);
    --shadow-hover: 0 10px 30px hsl(0 0% 0% / 0.50);
}

/* Reset & Base */
html {
    font-size: 14px;
}

*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: var(--font-sans);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    background: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    min-height: 100vh;
    transition: background var(--transition), color var(--transition);
}

/* App Container */
.app {
    max-width: 1600px;
    margin: 0 auto;
    padding: 20px;
}

/* Header */
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 18px 24px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    margin-bottom: 16px;
}

.logo {
    color: var(--text-primary);
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0;
}

.header-right {
    display: flex;
    align-items: center;
    gap: 12px;
}

.theme-toggle {
    background: var(--surface-interactive);
    border: none;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background var(--transition), transform var(--press);
}

.theme-toggle:hover {
    background: var(--surface-interactive-hover);
}

.theme-toggle:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

.theme-toggle:active {
    transform: scale(0.94);
}

.theme-icon {
    font-size: 1.2rem;
    color: var(--text-primary);
}

/* Tabs */
.tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 16px;
    background: var(--bg-secondary);
    padding: 5px;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow);
}

.tab {
    flex: 1;
    padding: 12px 24px;
    background: transparent;
    border: none;
    border-radius: var(--radius);
    cursor: pointer;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text-secondary);
    box-shadow: inset 0 -2px 0 transparent;
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}

.tab:hover {
    background: var(--surface-sunken);
    color: var(--text-primary);
}

.tab:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: -2px;
}

/* Current selection — quiet tint + accent text + 2px underline, never a loud fill */
.tab.active {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
    box-shadow: inset 0 -2px 0 var(--accent-text);
}

/* Tab Content */
.tab-content {
    display: none;
}

.tab-content.active {
    display: block;
}

/* Cards */
.card {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 28px;
    box-shadow: var(--shadow);
}

.card h2 {
    margin-bottom: 24px;
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

/* Flow-card header row: title on the left, an action (e.g. "View Flow") on
   the right. Reused by every wizard card so the header layout stays one rule. */
.flow-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
}
.flow-card-header h2 { margin: 0; }

/* Flow cards span the full content width, aligned with the header and tabs.
   Wayfinding/structure is carried by the sticky stepper rather than a
   narrowed column. */

/* ============================================================
   Stepper — persistent wayfinding for multi-step flows.
   The wizard sets done/current/upcoming on each step (see wizard.js):
   done = an earlier step, current = the visible step, upcoming = a
   later step. Sticky so "where am I / what's next" stays visible on
   a long scroll.
   ============================================================ */
.stepper {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    gap: 0;
    list-style: none;
    margin: 0 0 28px;
    padding: 16px 12px;
    background: var(--surface-sunken);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
}

.stepper .step {
    position: relative;
    flex: 1;
    min-width: 0;
}

/* Progress rail: a track segment running from this node's centre to the next
   node's. Sits behind the solid markers; turns accent once the step is done. */
.stepper .step:not(:last-child)::after {
    content: '';
    position: absolute;
    top: 15px;
    left: 50%;
    width: 100%;
    height: 2px;
    background: var(--border-strong);
    z-index: 0;
}
.stepper .step.done::after { background: var(--accent-primary); }

.stepper .step-btn {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 0 6px;
    background: transparent;
    border: none;
    cursor: default;
    text-align: center;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 0.8rem;
    transition: color var(--transition);
}

.stepper .step-marker {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: 2px solid var(--border-strong);
    background: var(--bg-secondary);
    color: var(--text-muted);
    font-size: 0.82rem;
    font-weight: 600;
    line-height: 1;
    transition: background var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);
}

.stepper .step-label {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: center;
}

/* Reachable steps (done / current) are interactive — click to jump back */
.stepper .step.done .step-btn,
.stepper .step.current .step-btn {
    cursor: pointer;
}
.stepper .step-btn:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 1px;
}
.stepper .step.done .step-btn:hover .step-label,
.stepper .step.current .step-btn:hover .step-label {
    color: var(--text-primary);
}
.stepper .step.done .step-btn:hover .step-marker {
    box-shadow: 0 0 0 4px var(--accent-tint);
}

/* done — calm: outline circle with a check, secondary label */
.stepper .step.done .step-marker {
    background: var(--bg-secondary);
    border-color: var(--accent-primary);
    color: var(--accent-text);
    font-size: 0;
}
.stepper .step.done .step-marker::after {
    content: '\2713';                 /* ✓ */
    font-size: 0.85rem;
    font-weight: 700;
}
.stepper .step.done .step-label { color: var(--text-secondary); }

/* current — the focal node: filled marker + soft halo ring, accent bold label.
   No boxed pill; the node and label colour carry "you are here". */
.stepper .step.current .step-label {
    color: var(--accent-text);
    font-weight: 600;
}
.stepper .step.current .step-marker {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: #fff;
    box-shadow: 0 0 0 4px var(--accent-tint);
}

/* Sections get scroll offset so the sticky stepper never covers a heading */
.flow-card .form-section { scroll-margin-top: 84px; }

/* Inside a wizard only one step shows at a time, so the inter-section
   dividers are dropped — the single separator is the wizard footer's top
   border. Prevents the "two faint lines with a gap" look. */
.wizard .form-section {
    border-bottom: none;
    padding-bottom: 0;
    margin-bottom: 4px;
}

/* Dependency add row — inputs flex, Add button stays compact */
.dep-add-row {
    display: flex;
    gap: 16px;
    align-items: flex-end;
    margin-bottom: 16px;
}
.dep-add-row .form-group {
    flex: 1;
    margin: 0;
}
.dep-add-row .btn {
    flex: 0 0 auto;
}

/* Inline step loading indicator (e.g. fetching build info) */
.step-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 56px 24px;
    color: var(--accent-text);
    font-size: 0.95rem;
    font-weight: 600;
}
.step-loading .loading-spinner {
    width: 28px;
    height: 28px;
    border-width: 3px;
}
.step-loading-text { letter-spacing: 0.2px; }

/* Pleasant wizard motion. Entering elements fade and rise a touch (ease-out,
   never from scale(0)) so steps settle in instead of snapping. The global
   prefers-reduced-motion rule neutralizes these automatically. */
@keyframes qa-step-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* A new step fades+rises in. Replays on each step change because the wizard
   toggles display:none, which restarts the animation. Scoped to the QA build
   and Release New Build wizards so other flows are untouched. */
#qa-build .form-section:not(.hidden),
#release-build .form-section:not(.hidden) {
    animation: qa-step-in 260ms var(--ease-out) both;
}

/* After the loader resolves, the build-info fields cascade in (added by JS as
   .qa-reveal). This is a one-time, post-load moment, so it's a touch slower and
   travels further than the step transition — it should read as a settle, not a
   pop. The cascade runs field-by-field, not block-by-block, so it's clearly
   sequential rather than "everything at once". */
@keyframes qa-reveal-in {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
}
.qa-reveal .form-row > .form-group,
.qa-reveal > .form-group {
    animation: qa-reveal-in 420ms var(--ease-out) both;
}
.qa-reveal .form-row > .form-group:nth-child(1) { animation-delay: 60ms; }
.qa-reveal .form-row > .form-group:nth-child(2) { animation-delay: 140ms; }
.qa-reveal .form-row > .form-group:nth-child(3) { animation-delay: 220ms; }
.qa-reveal > #jiraRefsGroup { animation-delay: 300ms; }   /* Jira tickets (when shown) */
.qa-reveal > .form-group     { animation-delay: 360ms; }   /* Release notes (last block) */

/* Wizard footer — Back on the left, Next/Submit on the right */
.wizard-nav {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 28px;
    padding-top: 20px;
    border-top: 1px solid var(--border-color);
}
.wizard-nav .nav-right {
    display: flex;
    gap: 12px;
    margin-left: auto;
}

/* Why Next is blocked. Sits directly above the nav so the answer is where the
   dead button is — a disabled Next with no explanation is a guessing game.
   Warning tone, not danger: nothing is wrong yet, the step just isn't finished. */
/* A Next that can't advance yet. Looks like a disabled button but stays
   clickable — clicking it reveals why (a disabled button can't answer). */
.btn.is-blocked {
    opacity: 0.5;
}

.wizard-blockers {
    margin-top: 24px;
    padding: 11px 14px;
    border: 1px solid var(--warning);
    border-radius: var(--radius);
    background: var(--warning-bg);
}

.wizard-blockers:focus { outline: none; }

.wizard-blockers__title {
    display: block;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--warning);
}

.wizard-blockers__list {
    margin: 6px 0 0;
    padding-left: 20px;
    font-size: 0.83rem;
    line-height: 1.55;
    color: var(--text-primary);
}

.wizard-blockers__list li + li { margin-top: 3px; }

/* Verify step — read-only review of the build before it is triggered. */
.verify-summary {
    margin: 4px 0 0;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    overflow: hidden;
}
.verify-summary .verify-row {
    display: grid;
    grid-template-columns: 180px 1fr;
    gap: 16px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-color);
}
.verify-summary .verify-row:last-child { border-bottom: none; }
.verify-summary .verify-row:nth-child(odd) { background: var(--surface-sunken); }
.verify-summary dt {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-secondary);
}
.verify-summary dd {
    margin: 0;
    font-size: 0.9rem;
    color: var(--text-primary);
    word-break: break-word;
}
.verify-summary dd code {
    font-family: var(--font-mono);
    font-size: 0.82rem;
}
.verify-summary .verify-muted { color: var(--text-muted); }
.verify-summary .verify-row.verify-warning {
    background: var(--warning-bg);
    border-bottom-color: var(--warning);
}
.verify-summary .verify-row.verify-warning dt {
    color: var(--warning);
}
.verify-summary .verify-row.verify-warning dd {
    display: flex;
    gap: 10px;
    align-items: flex-start;
}
.verify-warning__icon {
    color: var(--warning);
    flex-shrink: 0;
    line-height: 1.35;
}

/* Build source — keep the Start Build button off the build-plan table. */
#buildSection .verify-summary,
#rbBuildSection .verify-summary { margin-bottom: 16px; }

/* Template settings actions — keep Preview / Save Template off the editor above. */
.template-actions { margin-top: 16px; }

/* Dependencies empty state — makes it clear nothing is added until "Add". */
.dep-empty-hint { margin: 4px 0 0; font-size: 13px; color: var(--text-muted); font-style: italic; }

/* ── Ticket Issues modal ──────────────────────────────────────────────
   Make each problem scannable: what field is wrong, its current value vs
   the expected one, and whether it auto-fixes or needs a manual Jira edit. */
.ti-intro { margin: 0 0 16px; font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
.ti-intro strong { color: var(--text-primary); }
.ti-intro code,
.ti-foot code {
    font-family: var(--font-mono); font-size: 0.85em;
    background: var(--surface-sunken); color: var(--text-primary);
    padding: 1px 6px; border-radius: 4px;
}

.ticket-issues {
    list-style: none; margin: 0; padding: 0;
    max-height: 60vh; overflow-y: auto;
    display: flex; flex-direction: column; gap: 10px;
}
.ti-card {
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--bg-secondary);
    padding: 12px 14px;
}

.ti-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ti-head input[type="checkbox"] {
    width: 16px; height: 16px; flex: none; margin: 0;
    cursor: pointer; accent-color: var(--accent-primary);
}
.ti-key {
    font-weight: 700; font-family: var(--font-mono);
    color: var(--accent-text); text-decoration: none;
}
.ti-key:hover { text-decoration: underline; }
.ti-summary { color: var(--text-primary); font-size: 14px; }
label.ti-summary { cursor: pointer; }

.ti-issues { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.ti-issue {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: 6px;
    background: var(--surface-sunken);
}
.ti-issue--status { background: var(--warning-bg); }
/* An empty Project (assets). Warning-toned like the status row - both are a
   Jira field to correct, not a broken build. */
.ti-issue--assets { background: var(--warning-bg); }
.ti-issue-icon { flex: none; font-size: 13px; line-height: 1; }
.ti-issue-body {
    flex: 1; min-width: 0;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 8px;
    font-size: 13px;
}
.ti-field { font-weight: 600; color: var(--text-secondary); }
.ti-change { display: inline-flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.ti-current {
    font-family: var(--font-mono); font-size: 12px;
    color: hsl(var(--status-error-text));
    text-decoration: line-through;
    text-decoration-color: hsl(var(--status-error-text) / 0.55);
    word-break: break-word;
}
.ti-arrow { color: var(--text-muted); }
.ti-expected {
    font-family: var(--font-mono); font-size: 12px; font-weight: 600;
    color: hsl(var(--status-success-text)); word-break: break-word;
}
.ti-msg { color: var(--text-secondary); }

.ti-badge {
    flex: none; font-size: 11px; font-weight: 600;
    padding: 2px 9px; border-radius: 999px; white-space: nowrap;
}
.ti-badge--auto {
    color: hsl(var(--status-success-text));
    background: hsl(var(--status-success-bg));
    border: 1px solid hsl(var(--status-success-border));
}
.ti-badge--manual {
    color: hsl(var(--status-warning-text));
    background: hsl(var(--status-warning-bg));
    border: 1px solid hsl(var(--status-warning-border));
}

.ti-foot { margin: 16px 0 0; font-size: 13px; line-height: 1.5; color: var(--text-secondary); }
.ti-foot strong { color: var(--text-primary); }

@media (max-width: 768px) {
    .verify-summary .verify-row { grid-template-columns: 1fr; gap: 4px; }
}

/* Verify step — preview of the exact notification email. */
.verify-email { margin-top: 22px; }
.verify-email-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 8px;
}
.verify-email-head {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 16px;
    background: var(--surface-sunken);
    border: 1px solid var(--border-color);
    border-radius: var(--radius) var(--radius) 0 0;
    font-size: 0.9rem;
    color: var(--text-primary);
}
.verify-email-head .verify-email-key {
    display: inline-block;
    width: 64px;
    color: var(--text-muted);
    font-size: 0.8rem;
}
.verify-email-frame {
    display: block;
    width: 100%;
    height: 420px;
    border: 1px solid var(--border-color);
    border-top: none;
    border-radius: 0 0 var(--radius) var(--radius);
    background: #fff;
}
.verify-email-loading {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 0;
    color: var(--accent-text);
    font-size: 0.9rem;
    font-weight: 500;
}
.verify-email-off {
    padding: 10px 0;
    color: var(--text-muted);
    font-size: 0.9rem;
}
.verify-email-note-hint {
    margin: 8px 0 0 0;
    color: var(--text-muted);
    font-size: 0.8rem;
    line-height: 1.4;
}
/* Manager link-verification panel on the Verify step */
.verify-links {
    margin-top: 12px;
    padding: 10px 14px;
    background: var(--warning-bg);
    border: 1px solid var(--warning);
    border-radius: var(--radius);
}
.verify-links-title {
    font-size: 0.85rem; font-weight: 600;
    color: var(--text-primary); margin-bottom: 6px; line-height: 1.4;
}
.verify-links-list { margin: 0; padding-left: 18px; font-size: 0.88rem; line-height: 1.7; }
.verify-links-list a { color: var(--accent-text); text-decoration: underline; }
/* (.release-tracker-guidance was merged into .tracker-brief — one panel.) */

/* Finalize Sprint confirmation modal */
.finalize-confirm { font-size: 0.95rem; }
.finalize-warning {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 10px 14px;
    margin: 0 0 16px 0;
    background: var(--warning-bg);
    border: 1px solid var(--warning);
    border-radius: var(--radius);
    color: var(--text-primary);
    font-size: 0.88rem;
    line-height: 1.45;
}
.finalize-warning-icon { color: var(--warning); font-size: 1.05rem; line-height: 1.35; flex-shrink: 0; }
.finalize-intro { margin: 0 0 16px 0; color: var(--text-primary); }
.finalize-label {
    margin: 0 0 8px 0;
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--text-muted);
}
.finalize-projects { list-style: none; padding: 0; margin: 0 0 18px 0; }
.finalize-project {
    padding: 10px 12px; margin-bottom: 8px;
    background: var(--surface-sunken);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
}
.finalize-project:last-child { margin-bottom: 0; }
.finalize-project-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.finalize-project-name { font-weight: 600; color: var(--text-primary); }
.finalize-version {
    font-family: var(--font-mono);
    font-size: 0.82rem;
    padding: 2px 9px; border-radius: 999px;
    background: var(--bg-tertiary); border: 1px solid var(--border-color);
    color: var(--accent-text); white-space: nowrap;
}
.finalize-steps { margin-top: 5px; font-size: 0.83rem; color: var(--text-secondary); line-height: 1.4; }
.finalize-next {
    margin: 0; padding-left: 18px;
    color: var(--text-secondary); font-size: 0.87rem; line-height: 1.65;
}
.finalize-next strong { color: var(--text-primary); font-weight: 600; }
.verify-email-preview-note {
    margin-bottom: 10px;
    padding: 10px 14px;
    background: var(--warning-bg);
    border: 1px solid var(--warning);
    border-radius: var(--radius);
    color: var(--text-primary);
    font-size: 0.85rem;
    line-height: 1.45;
}
.verify-email-preview-note strong { color: var(--text-primary); }

@media (max-width: 768px) {
    .stepper .step-label { display: none; }
}

/* Form Elements */
.form-section {
    margin-bottom: 32px;
    padding-bottom: 24px;
    border-bottom: 1px solid var(--border-color);
}

.form-section:last-child {
    border-bottom: none;
    margin-bottom: 0;
}

.form-section h3 {
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 600;
    margin-bottom: 16px;
    color: var(--text-primary);
}

.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin-bottom: 16px;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.form-group label {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
}

.form-group input,
.form-group select,
.form-group textarea {
    padding: 10px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    font-size: 0.95rem;
    background: var(--bg-primary);
    color: var(--text-primary);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--accent-text);
    box-shadow: 0 0 0 3px var(--accent-ring);
}

.form-group input:disabled,
.form-group select:disabled {
    background: var(--surface-sunken);
    cursor: not-allowed;
    opacity: 0.6;
}

/* Native selects — custom chevron + clear affordance, themed both ways */
.form-group select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    cursor: pointer;
    padding-right: 38px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235a616b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
}

.form-group select:hover:not(:disabled) {
    border-color: var(--border-strong);
}

.form-group select:disabled {
    cursor: not-allowed;
}

[data-theme="dark"] .form-group select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23aab0b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}

/* ============================================================
   Custom select (ui-select). The native <select> stays in the DOM as the
   value source (transparent, pointer-events: none) so existing JS and tests
   keep working; a styled trigger + popup overlay provide the visible UI,
   type-ahead search, and keyboard navigation. See js/ui-select.js.
   ============================================================ */
.ui-select { position: relative; }
.ui-select > select {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}
/* The disabled VISUAL lives on the trigger; keep the value-holder fully
   hidden even when disabled. Otherwise `.form-group select:disabled`
   (opacity:0.6) out-specifies the rule above and the native control —
   its text + chevron — bleeds through the trigger. */
.ui-select > select:disabled {
    opacity: 0;
}
.ui-select-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--bg-primary);
    color: var(--text-primary);
    font-family: inherit;
    font-size: 0.95rem;
    line-height: 1.4;
    cursor: pointer;
    text-align: left;
    transition: border-color var(--transition), box-shadow var(--transition);
}
.ui-select-trigger:hover:not(:disabled) { border-color: var(--border-strong); }
.ui-select.open .ui-select-trigger,
.ui-select-trigger:focus-visible {
    outline: none;
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 3px var(--accent-ring);
}
.ui-select-trigger:disabled {
    background: var(--surface-sunken);
    opacity: 0.6;
    cursor: not-allowed;
}
.ui-select-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-select-trigger.is-placeholder .ui-select-value { color: var(--text-muted); }
.ui-select-caret {
    flex: 0 0 auto;
    display: inline-flex;
    color: var(--text-muted);
    transition: transform var(--transition);
}
.ui-select.open .ui-select-caret { transform: rotate(180deg); }

.ui-select-popup {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 60;
    display: flex;
    flex-direction: column;
    max-height: 290px;
    padding: 4px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    box-shadow: var(--shadow-hover);
}
.ui-select-popup[hidden] { display: none; }
.ui-select-popup:focus { outline: none; }
.ui-select-group {
    padding: 12px 10px 6px;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}
.ui-select-group:not(:first-child) {
    margin-top: 6px;
    border-top: 1px solid var(--border-color);
    padding-top: 12px;
}
.ui-select-list { overflow-y: auto; }
.ui-select-option {
    padding: 9px 10px;
    border-radius: 6px;
    font-size: 0.9rem;
    color: var(--text-primary);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Hover (mouse) and active (keyboard) share a clear highlight */
.ui-select-option:hover,
.ui-select-option.active {
    background: var(--surface-interactive);
    color: var(--text-primary);
}
.ui-select-option[aria-selected="true"] {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
}
.ui-select-option[aria-selected="true"]:hover,
.ui-select-option[aria-selected="true"].active {
    background: var(--accent-tint-strong);
    color: var(--accent-text);
}
.ui-select-empty {
    padding: 12px;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.9rem;
}

.form-group textarea {
    resize: vertical;
    font-family: var(--font-mono);
    font-size: 0.85rem;
    white-space: pre-wrap;
    line-height: 1.5;
}

/* Free-text "Reason" fields (skip-step modals) read as prose, not code —
   override the monospace .form-group textarea used by the changelog editors. */
.form-group textarea.reason-field {
    font-family: inherit;
    font-size: 0.9rem;
    min-height: 76px;
}

/* Quill Editor */
.quill-editor {
    background: var(--bg-primary);
    border-radius: var(--radius);
    min-height: 200px;
}

.quill-editor .ql-toolbar {
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius) var(--radius) 0 0;
}

.quill-editor .ql-container {
    border: 1px solid var(--border-color);
    border-top: none;
    border-radius: 0 0 var(--radius) var(--radius);
    font-size: 0.95rem;
    min-height: 150px;
}

.quill-editor .ql-editor {
    min-height: 150px;
    color: var(--text-primary);
}

.quill-editor .ql-editor.ql-blank::before {
    color: var(--text-muted);
    font-style: normal;
}

#releaseNotesEditor {
    min-height: 180px;
}

#templateBodyEditor {
    min-height: 250px;
}

/* Dark mode Quill overrides.
   Quill inserts .ql-toolbar as a SIBLING of the .quill-editor (which becomes the
   .ql-container), not a child — so toolbar rules MUST anchor on .ql-toolbar. A
   `.quill-editor .ql-toolbar` descendant selector never matches, which left the
   toolbar icons at Quill's default dark #444 (invisible on the dark canvas). */
[data-theme="dark"] .ql-snow.ql-toolbar {
    background: var(--bg-tertiary);
    border-color: var(--border-color);
}

[data-theme="dark"] .quill-editor.ql-snow {
    border-color: var(--border-color);
}

[data-theme="dark"] .ql-snow.ql-toolbar .ql-stroke {
    stroke: var(--text-secondary);
}

[data-theme="dark"] .ql-snow.ql-toolbar .ql-fill {
    fill: var(--text-secondary);
}

[data-theme="dark"] .ql-snow.ql-toolbar .ql-picker-label {
    color: var(--text-secondary);
}

/* Hover / active formats lift to the accent so the current state is legible. */
[data-theme="dark"] .ql-snow.ql-toolbar button:hover .ql-stroke,
[data-theme="dark"] .ql-snow.ql-toolbar button.ql-active .ql-stroke,
[data-theme="dark"] .ql-snow.ql-toolbar .ql-picker-label:hover,
[data-theme="dark"] .ql-snow.ql-toolbar .ql-picker-label.ql-active {
    stroke: var(--accent-text);
    color: var(--accent-text);
}

[data-theme="dark"] .ql-snow.ql-toolbar button:hover .ql-fill,
[data-theme="dark"] .ql-snow.ql-toolbar button.ql-active .ql-fill {
    fill: var(--accent-text);
}

[data-theme="dark"] .ql-snow .ql-picker-options {
    background: var(--bg-secondary);
    border-color: var(--border-color);
}

[data-theme="dark"] .ql-snow .ql-picker-item {
    color: var(--text-primary);
}

[data-theme="dark"] .ql-snow .ql-picker-item:hover,
[data-theme="dark"] .ql-snow .ql-picker-item.ql-selected {
    color: var(--accent-text);
}

.checkbox-group {
    flex-direction: row;
    align-items: center;
    gap: 8px;
}

.checkbox-group input[type="checkbox"] {
    width: 18px;
    height: 18px;
    cursor: pointer;
}

/* Checked state reads in the brand accent, not the OS default blue. */
input[type="checkbox"] {
    accent-color: var(--accent-primary);
}

input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

.checkbox-group label {
    cursor: pointer;
    font-size: 0.95rem;
}

.hint {
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-bottom: 12px;
}

.info-text {
    font-size: 0.95rem;
    color: var(--text-primary);
    padding: 10px 0;
}

/* Buttons */
.btn {
    padding: 10px 20px;
    border: none;
    border-radius: var(--radius);
    font-size: 0.95rem;
    font-weight: 500;
    cursor: pointer;
    transition: background var(--transition), color var(--transition),
                box-shadow var(--transition), transform var(--press);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Press feedback (emil): the button physically yields to a click. */
.btn:active:not(:disabled) {
    transform: scale(0.97);
}

.btn:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

.btn-primary {
    background: var(--accent-primary);
    color: #fff;
    box-shadow: 0 1px 2px hsl(208 55% 26% / 0.30);
}

.btn-primary:hover:not(:disabled) {
    background: var(--accent-primary-hover);
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
}

.btn-secondary {
    background: var(--surface-interactive);
    color: var(--text-primary);
}

.btn-secondary:hover:not(:disabled) {
    background: var(--surface-interactive-hover);
}

.btn-danger {
    background: var(--danger);
    color: #fff;
}

.btn-danger:hover:not(:disabled) {
    background: var(--danger-hover);
}

/* Ghost — outlined secondary. A visible border at rest (not only on hover) so
   the affordance reads as a button without a fill competing with primary. */
.btn-ghost {
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--text-muted);
}

.btn-ghost:hover:not(:disabled) {
    background: var(--surface-sunken);
    color: var(--text-primary);
    border-color: var(--text-secondary);
}

/* Ghost-danger — Revert: danger is visible at rest (red text + red border),
   but it stays outlined, not a loud fill. The solid danger fill is reserved
   for the final Confirm in the revert dialog, so this never out-shouts the
   in-flight primary (Resume) while still reading unmistakably as destructive. */
.btn-ghost-danger {
    background: transparent;
    color: var(--danger);
    border: 1px solid var(--danger);
}

.btn-ghost-danger:hover:not(:disabled) {
    background: hsl(var(--status-error-bg));
    color: var(--danger);
    border-color: var(--danger);
}

.btn-warning {
    background: var(--warning);
    color: #333;
}

.btn-warning:hover:not(:disabled) {
    opacity: 0.9;
}

/* Jira Tickets List */
.jira-tickets-list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 12px;
    background: var(--bg-tertiary);
    border-radius: var(--radius);
    font-size: 13px;
}

.jira-tickets-list a {
    color: var(--accent-text);
    text-decoration: none;
    padding: 2px 8px;
    background: var(--bg-secondary);
    border-radius: 4px;
    border: 1px solid var(--border-color);
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.jira-tickets-list a:hover {
    background: var(--accent-primary);
    color: white;
    border-color: var(--accent-text);
}

/* Section header with actions */
.form-section .section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

.form-section .section-header h3 {
    margin-bottom: 0;
}

/* Warning modal styles */
[data-theme="dark"] .fix-ticket-checkbox {
    accent-color: var(--accent-text);
}

/* Info text as block */
.info-text {
    display: block;
    padding: 8px 12px;
    background: var(--bg-tertiary);
    border-radius: var(--radius);
    font-weight: 500;
}

/* Dark mode warning background */
[data-theme="dark"] {
    --bg-warning: #3d3a1d;
    --text-warning: #e0c866;
}

.btn-small {
    padding: 6px 12px;
    font-size: 0.85rem;
}

.btn-large {
    padding: 14px 32px;
    font-size: 1rem;
}

.btn-group {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

.btn-group .btn.active {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
    box-shadow: inset 0 0 0 1px var(--accent-text);
}

/* Data Tables */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.data-table th,
.data-table td {
    padding: 12px;
    text-align: left;
    border-bottom: 1px solid var(--border-color);
}

.data-table th {
    background: var(--bg-tertiary);
    font-weight: 600;
    color: var(--text-secondary);
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.data-table tbody tr:hover {
    background: var(--surface-sunken);
}

.data-table .loading {
    text-align: center;
    color: var(--text-muted);
    padding: 24px;
}

.data-table .actions {
    display: flex;
    gap: 8px;
}

/* Settings Sections */
.settings-section {
    margin-bottom: 32px;
    padding-bottom: 24px;
    border-bottom: 1px solid var(--border-color);
}

.settings-section:last-child {
    border-bottom: none;
}

.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

.section-header h3 {
    margin: 0;
}

/* A label with a quiet action on its right (e.g. "Re-read from repo" next to
   Release version). The action is secondary to the field, so it reads as a
   link, not a button competing with the form. */
.field-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.btn-link {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 0.78rem;
    color: var(--accent-primary);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.btn-link:hover:not(:disabled) { color: var(--accent-primary-hover, var(--accent-primary)); }
.btn-link:disabled { opacity: 0.6; cursor: default; text-decoration: none; }
.btn-link:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: 3px;
}

/* Copy-from-previous-release action, above the project checkbox list. A quiet
   affordance: it shortcuts setup, it isn't the primary way to start a release. */
.sprint-copy-row {
    display: flex;
    margin-bottom: 12px;
}

/* Template Tabs */
.template-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 16px;
}

.template-tab {
    padding: 8px 16px;
    background: var(--surface-interactive);
    border: none;
    border-radius: var(--radius);
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}

.template-tab:hover {
    background: var(--surface-interactive-hover);
}

.template-tab.active {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
    box-shadow: inset 0 0 0 1px var(--accent-text);
}

/* Modals */
.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    opacity: 1;
    visibility: visible;
    transition: opacity 220ms var(--ease-out), visibility 0s;
}

/* Override the global `.hidden { display:none }` for the overlay so the opacity
   + scale transition can actually play on open AND close (display:none would
   skip it). When hidden the overlay is transparent and click-through;
   visibility flips off only after the fade so it leaves the tab order cleanly. */
.modal-overlay.hidden {
    display: flex !important;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 200ms var(--ease-out), visibility 0s linear 200ms;
}

/* Column layout, not a magic height: the header and footer take what they
   need and the body absorbs the rest. The old `max-height: calc(90vh - 140px)`
   on the body assumed a one-line footer, so a taller one (an explainer above
   the buttons, a wrapped confirmation) pushed the buttons under `overflow:
   hidden` and clipped them off the dialog. */
.modal {
    display: flex;
    flex-direction: column;
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    width: 90%;
    max-width: 1000px;
    max-height: 90vh;
    overflow: hidden;
    box-shadow: var(--shadow-hover);
    transform: scale(1);
    transition: transform 220ms var(--ease-out);
}

.modal-header, .modal-footer { flex: none; }

.modal.modal-wide {
    max-width: 95vw;
}

.modal-overlay.hidden .modal {
    transform: scale(0.96);
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color);
}

.modal-header h3 {
    margin: 0;
    font-size: 1.1rem;
}

.modal-close {
    background: none;
    border: none;
    font-size: 1.5rem;
    color: var(--text-muted);
    cursor: pointer;
    padding: 4px;
    line-height: 1;
}

.modal-close:hover {
    color: var(--text-primary);
}

.modal-close:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
    border-radius: 4px;
}

.modal-body {
    padding: 20px;
    flex: 1 1 auto;
    min-height: 0;      /* a flex item defaults to min-content — without this it refuses to shrink and scroll */
    overflow-y: auto;
}

.modal-footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    border-top: 1px solid var(--border-color);
}

/* A footer button states an action; wrapping it mid-phrase ("Re-generate / and
   continue") turns a decision into a puzzle and grows the footer over the
   content. Labels stay on one line and the row wraps instead. */
.modal-footer .btn { white-space: nowrap; }

/* Anchor for the tracker explainer's floating panel. */
.modal-footer { position: relative; }

/* Whose PR is about to be waived. The action picks the first project still
   waiting, so in a multi-project release the dialog has to name it — the answer
   decides which code the release ships. */
.skip-target {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 14px;
    font-size: 0.9rem;
    color: var(--text-primary);
}

.skip-target__label {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.skip-target code {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--text-secondary);
}

/* ── "Already merged?" verification ──────────────────────────────────────
   The claim behind "release without a PR" decides which code gets tagged, so
   it is checked against GitHub and the answer is shown where the decision is
   made. Three outcomes, each carrying an icon + words: colour alone never
   states the verdict. */
.merge-check {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 14px 0 0;
    padding: 10px 12px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--surface-sunken);
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--text-secondary);
}

.merge-check code {
    font-family: var(--font-mono);
    font-size: 0.92em;
}

.merge-check__icon { flex: none; font-size: 0.95rem; line-height: 1.5; }

.merge-check--ok {
    border-color: hsl(var(--status-success-border));
    background: var(--success-bg);
    color: hsl(var(--status-success-text));
}

.merge-check--bad {
    border-color: hsl(var(--status-error-border));
    background: var(--danger-bg);
    color: hsl(var(--status-error-text));
}

.merge-check--unknown {
    border-color: hsl(var(--status-warning-border));
    background: var(--warning-bg);
    color: hsl(var(--status-warning-text));
}

.merge-check--ok b, .merge-check--bad b, .merge-check--unknown b { font-weight: 700; }

/* The spinner while GitHub is asked — motion is decorative here, the sentence
   next to it already says what is happening. */
.merge-check__spin {
    flex: none;
    width: 13px;
    height: 13px;
    margin-top: 3px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent-text);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}

/* The same verdict, condensed, kept on the project card after the dialog. */
.skip-verdict {
    margin: 6px 0 0;
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--text-muted);
}

.skip-verdict code { font-family: var(--font-mono); }
.skip-verdict--ok { color: var(--success); }
.skip-verdict--bad { color: var(--danger); font-weight: 600; }
.skip-verdict--unknown { color: var(--warning); }

@media (prefers-reduced-motion: reduce) {
    .merge-check__spin { animation: none; border-top-color: var(--border-strong); }
}

/* ── Release tracker brief: how this ships + what each control does ──────
   ONE panel. Two stacked boxes said overlapping things in identical styling,
   so the eye had to read both in full to find the difference. Here the prose
   is the situation, and the controls are a scannable key→meaning grid — each
   name rendered the way it appears in the footer, so "Continue flow" in the
   text and the button below are visibly the same thing. */
.tracker-brief {
    margin: 0 0 20px;
    padding: 16px 18px;
    border-radius: var(--radius-lg);
    background: var(--surface-sunken);
    border: 1px solid var(--border-color);
}

.tracker-brief__lead {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.6;
    color: var(--text-primary);
}

.tracker-brief__lead b { font-weight: 700; }

.tracker-brief__actions {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 8px 14px;
    align-items: baseline;
    margin: 14px 0 0;
    padding: 0;
    list-style: none;
}

.tracker-brief__actions li {
    display: contents;               /* the key and its meaning are grid cells */
}

.tracker-brief__actions li > span:last-child {
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--text-secondary);
}

/* The control's name, dressed as the control: an operator matches it to the
   footer without translating "the button called…". */
.tracker-brief__key {
    justify-self: start;
    padding: 3px 10px;
    border: 1px solid var(--accent-text);
    border-radius: var(--radius);
    background: var(--accent-tint);
    color: var(--accent-text);
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

.tracker-brief__safe {
    margin: 14px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--border-color);
    font-size: 0.78rem;
    color: var(--text-muted);
}

.tracker-brief__safe b { color: var(--text-secondary); font-weight: 600; }

/* ── Release tracker footer: explainer + confirmation ────────────────────
   The tracker's Continue re-runs a whole phase, so the footer carries its own
   reasoning. Collapsed it costs one line; open it answers "what happens if I
   press this" without leaving the release. */
.tracker-explain {
    position: static;                /* the panel anchors to the footer */
    flex: 1 1 100%;
    order: -1;                       /* the summary sits above the buttons */
    margin: 0;
    padding: 0;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

/* Floating panel, anchored to the top edge of the footer: opening it must not
   change the footer's height, or the buttons it explains get pushed out of the
   dialog. Scrolls on a short viewport instead of growing past the top. */
.tracker-explain__panel {
    position: absolute;
    bottom: calc(100% - 1px);
    left: 0;
    right: 0;
    z-index: 2;
    max-height: min(46vh, 340px);
    overflow-y: auto;
    padding: 14px 20px 16px;
    background: var(--bg-secondary);
    border-top: 1px solid var(--border-color);
    box-shadow: var(--shadow-md);
    animation: tracker-explain-in 0.16s var(--ease-out);
}

@keyframes tracker-explain-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .tracker-explain__panel { animation: none; }
}

.tracker-explain > summary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: fit-content;
    padding: 4px 8px 4px 2px;
    border-radius: var(--radius);
    color: var(--accent-text);
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    transition: background var(--transition), color var(--transition);
}

.tracker-explain > summary::-webkit-details-marker { display: none; }

/* Own disclosure caret — rotates with the state, so the affordance is visible
   before the click and the direction is unambiguous after it. */
.tracker-explain > summary::before {
    content: "";
    width: 0;
    height: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transition: transform 0.16s var(--ease-out);
}

.tracker-explain[open] > summary::before { transform: rotate(90deg); }
.tracker-explain > summary:hover { background: var(--accent-tint); }
.tracker-explain > summary:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.tracker-explain__steps {
    margin: 0;
    padding-left: 20px;
    line-height: 1.65;
}

.tracker-explain__steps li + li { margin-top: 4px; }
.tracker-explain__steps b { color: var(--text-primary); }

/* The reassurance an operator is actually looking for: what re-running costs. */
.tracker-explain__safe {
    margin: 8px 0 0;
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--text-muted);
}

.tracker-explain__safe span { margin-right: 4px; color: var(--accent-text); }

/* The question itself stays short — the detail is one line above — so the two
   buttons keep their full labels on one row. */
.tracker-confirm {
    flex: 1 1 260px;
    min-width: 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-secondary);
}

@media (prefers-reduced-motion: reduce) {
    .tracker-explain > summary::before { transition: none; }
}

/* ============================================================
   Success confirmation block (QA build started, etc.)
   A drawn checkmark + a calm "what happens next" line. Motion
   collapses to the final, fully-drawn state under the global
   prefers-reduced-motion rule, so the check still reads with
   motion off (PRODUCT.md a11y target).
   ============================================================ */
.qa-success {
    text-align: center;
    padding: 4px 0 2px;
}

.qa-success__check {
    display: block;
    width: 60px;
    height: 60px;
    margin: 0 auto 18px;
    animation: qa-check-pop 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.qa-success__check circle {
    fill: none;
    stroke: var(--success, #28a745);
    stroke-width: 2.5;
    stroke-dasharray: 151;
    stroke-dashoffset: 151;
    animation: qa-check-draw 0.5s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}

.qa-success__check path {
    fill: none;
    stroke: var(--success, #28a745);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 40;
    stroke-dashoffset: 40;
    animation: qa-check-draw 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.45s forwards;
}

.qa-success__msg {
    margin: 0 auto;
    max-width: 44ch;
    color: var(--text-secondary);
    line-height: 1.55;
}

.qa-success__steps {
    list-style: none;
    margin: 18px 0 0;
    padding: 12px 14px;
    background: var(--surface-sunken);
    border-radius: var(--radius);
    text-align: left;
}

.qa-success__step {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--text-secondary);
}

.qa-success__step + .qa-success__step {
    margin-top: 8px;
}

.qa-success__step-icon {
    flex: none;
    font-weight: 700;
}

.qa-success__step--ok .qa-success__step-icon {
    color: var(--success, #28a745);
}

.qa-success__step--fail .qa-success__step-icon {
    color: var(--danger, #dc3545);
}

@keyframes qa-check-pop {
    0% { transform: scale(0.6); opacity: 0; }
    100% { transform: scale(1); opacity: 1; }
}

@keyframes qa-check-draw {
    to { stroke-dashoffset: 0; }
}

/* ============================================================
   Workflow "View Flow" diagrams — plain-language, light-theme
   step flow. Replaces the old dark ASCII canvas; the job is to
   explain the flow at a glance, so it leans on hierarchy and a
   single connector line rather than decoration.
   ============================================================ */
.flow-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 18px;
    padding: 4px;
    background: var(--surface-sunken);
    border-radius: var(--radius);
}

.flow-tab {
    flex: 1 1 auto;
    padding: 8px 12px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.flow-tab:hover:not(.active) {
    background: var(--surface-interactive);
    color: var(--text-primary);
}

.flow-tab.active {
    background: var(--accent-primary);
    color: #fff;
}

.flow-tab:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

.flow-lead {
    margin: 0 auto 18px;
    max-width: 620px;
    color: var(--text-secondary);
    font-size: 0.9rem;
    line-height: 1.5;
}

.flow {
    list-style: none;
    margin: 0 auto;
    padding: 0;
    max-width: 620px;
}

.flow-step {
    display: flex;
    gap: 14px;
    padding-bottom: 18px;
}

.flow-step:last-child {
    padding-bottom: 0;
}

.flow-step__marker {
    position: relative;
    flex: none;
    width: 28px;
    display: flex;
    justify-content: center;
}

/* Connector line between consecutive step markers. */
.flow-step:not(:last-child) .flow-step__marker::after {
    content: '';
    position: absolute;
    top: 30px;
    bottom: -18px;
    width: 2px;
    background: var(--border-color);
}

.flow-step__num {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--accent-tint);
    color: var(--accent-text);
    font-size: 0.8rem;
    font-weight: 700;
    z-index: 1;
}

.flow-step__num--final {
    background: var(--success, #28a745);
    color: #fff;
}

.flow-step__body {
    flex: 1;
    padding-top: 2px;
    min-width: 0;
}

.flow-step__title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
}

.flow-step__desc {
    margin-top: 3px;
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--text-secondary);
}

.flow-branch {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 10px;
}

.flow-branch__opt {
    flex: 1 1 180px;
    padding: 10px 12px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--bg-secondary);
}

.flow-branch__opt-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-primary);
}

.flow-branch__opt-desc {
    margin-top: 2px;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-secondary);
}

.flow-note {
    display: flex;
    gap: 7px;
    margin-top: 10px;
    padding: 8px 10px;
    background: var(--surface-sunken);
    border-radius: var(--radius);
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-secondary);
}

.flow-note__icon {
    flex: none;
    color: var(--accent-text);
    font-weight: 700;
}

/* Toast Notifications */
.toast-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1001;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.toast {
    padding: 14px 20px;
    border-radius: var(--radius);
    color: white;
    /* Solid default background so an untyped/info toast is never transparent
       (showToast emits `toast-<type>` classes; the variants below cover the
       dash-form too). */
    background: var(--accent-primary);
    font-size: 0.95rem;
    box-shadow: var(--shadow-hover);
    animation: slideIn 0.3s ease;
}

.toast.success, .toast-success {
    background: var(--success);
}

.toast.error, .toast-error {
    background: var(--danger);
}

.toast.warning, .toast-warning {
    background: var(--warning);
    color: #333;
}

.toast.info, .toast-info {
    background: var(--accent-primary);
}

@keyframes slideIn {
    from {
        transform: translateX(100%);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

/* Login Overlay */
.login-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--bg-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
}

.login-overlay.hidden {
    display: none;
}

.login-card {
    background: var(--bg-secondary);
    padding: 48px;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-hover);
    text-align: center;
    max-width: 420px;
    width: 90%;
}

.login-card h1 {
    color: var(--text-primary);
    font-family: var(--font-display);
    font-size: 2rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin-bottom: 8px;
}

.login-subtitle {
    color: var(--text-secondary);
    margin-bottom: 32px;
}

.login-card .form-group {
    text-align: left;
    margin-bottom: 24px;
}

.login-card .btn-large {
    width: 100%;
}

.login-hint {
    margin-top: 16px;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.login-error {
    margin-top: 16px;
    padding: 12px;
    background: hsl(var(--status-error-bg));
    border: 1px solid hsl(var(--status-error-border));
    border-radius: var(--radius);
    color: hsl(var(--status-error-text));
    font-size: 0.9rem;
}

.login-error.success {
    background: hsl(var(--status-success-bg));
    border-color: hsl(var(--status-success-border));
    color: hsl(var(--status-success-text));
}

/* User email in header */
.user-email {
    color: var(--text-secondary);
    font-size: 0.85rem;
    margin-right: 8px;
}

/* Hidden Utility */
.hidden {
    display: none !important;
}

/* Coming Soon */
.coming-soon {
    text-align: center;
    color: var(--text-muted);
    padding: 60px 20px;
    font-size: 1.1rem;
}

/* Image Info Box */
.image-info {
    margin-top: 16px;
    padding: 16px;
    background: var(--bg-tertiary);
    border-radius: var(--radius);
    border: 1px solid var(--border-color);
}

.image-info input[readonly] {
    background: var(--bg-secondary);
    font-family: var(--font-mono);
    font-size: 0.85rem;
}

/* Dependencies List */
.dependencies-list {
    list-style: none;
    margin-top: 12px;
}

.dependencies-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    background: var(--bg-tertiary);
    border-radius: var(--radius);
    margin-bottom: 8px;
}

.dependencies-list .dep-info {
    display: flex;
    gap: 16px;
}

.dependencies-list .dep-name {
    font-weight: 500;
}

.dependencies-list .dep-version {
    color: var(--text-muted);
    font-family: var(--font-mono);
}

.dependencies-list .dep-remove {
    background: none;
    border: none;
    color: var(--danger);
    cursor: pointer;
    font-size: 1.2rem;
}

/* Sprint Project Checkbox List */
.project-checkbox-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    /* A selected service grows downwards inside its own cell (the branch
       control). Without this the whole grid line grows with it. */
    align-items: start;
    gap: 8px;
}

.project-checkbox-list .group-label {
    grid-column: 1 / -1;
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-top: 12px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--border-color);
}

.project-checkbox-list .group-label:first-child {
    margin-top: 0;
}

.project-checkbox-list .project-checkbox-item {
    display: flex;
    align-items: center;
    gap: 8px;
    /* WCAG 2.2 AA touch target (DESIGN.md). Shared class — both screens. */
    min-height: 44px;
    padding: 8px 12px;
    background: var(--bg-primary);
    border-radius: var(--radius);
    cursor: pointer;
    transition: background var(--transition);
}

.project-checkbox-list .project-checkbox-item:hover {
    background: var(--surface-interactive);
}

/* Selected reads as Blue Tint plus a Deep Blue inset ring and label — the
   system's "current selection", not a second loud fill. `:has(input:checked)`
   takes the specificity of its argument, so it out-ranks a bare `:hover`
   whatever the source order: a checked row needs its own hover rule or it
   stops answering the pointer entirely. */
.project-checkbox-list .project-checkbox-item:has(input:checked) {
    background: var(--accent-tint);
    box-shadow: inset 0 0 0 1px var(--accent-text);
}

.project-checkbox-list .project-checkbox-item:has(input:checked) span {
    color: var(--accent-text);
    font-weight: 500;
}

.project-checkbox-list .project-checkbox-item:has(input:checked):hover {
    background: var(--accent-tint-strong);
}

.project-checkbox-list .project-checkbox-item:active {
    transform: scale(0.99);
}

/* The whole row is the click target, so the whole row is the focus target.
   --primary is a bare HSL triplet; used raw the declaration is invalid and the
   ring silently never paints. --accent-text is the sheet's focus-outline
   colour (see the checkbox and .radio-row rules). */
.project-checkbox-list .project-checkbox-item:has(input:focus-visible) {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

.project-checkbox-list .project-checkbox-item:has(input:disabled) {
    cursor: not-allowed;
    opacity: 0.55;
}

.project-checkbox-list .project-checkbox-item:has(input:disabled):hover {
    background: var(--bg-primary);
}

.project-checkbox-list .project-checkbox-item:has(input:disabled):active {
    transform: none;
}

.project-checkbox-list .project-checkbox-item input[type="checkbox"] {
    width: 16px;
    height: 16px;
    cursor: pointer;
}

.project-checkbox-list .project-checkbox-item span {
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--text-primary);
    user-select: none;
}

/* Sprint Project Card */
.sprint-project-card {
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    padding: 16px;
    margin-bottom: 16px;
    transition: box-shadow var(--transition);
}

.sprint-project-card:hover {
    box-shadow: var(--shadow);
}

.sprint-project-card .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-color);
}

.sprint-project-card .card-header h4 {
    margin: 0;
    font-size: 1rem;
    color: var(--accent-text);
}

.sprint-project-card .card-body .form-row {
    margin-bottom: 8px;
}

/* Groups a project card into labelled sections ("Release target", "Image to
   ship") so it's clear what each block configures. */
.sprint-card-section-label {
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-secondary);
    margin-top: 16px;
    margin-bottom: 2px;
}
.sprint-card-section-label:first-of-type { margin-top: 0; }
.sprint-project-card .card-body .sprint-card-section-label + .hint {
    margin-top: 0;
    margin-bottom: 8px;
}

/* Informational note inside a project card — e.g. "builds server-side after
   the PR merges" for hasDevelop projects, where there's no image to pick. */
.sprint-card-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 10px 12px;
    background: var(--surface-interactive);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    font-size: 0.85rem;
    color: var(--text-secondary);
    line-height: 1.4;
}
.sprint-card-note-icon { flex-shrink: 0; opacity: 0.8; }
.sprint-card-note strong { color: var(--text-primary); }

/* Release-wide option below the per-project cards (e.g. AI review on PRs). */
.sprint-release-option {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--border-color);
}

/* "?" help badge with a hover/focus tooltip (no JS). */
.help-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    margin-left: 6px;
    border-radius: 50%;
    border: 1px solid var(--border-color);
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1;
    color: var(--text-secondary);
    cursor: help;
    position: relative;
    user-select: none;
}
.help-icon:hover,
.help-icon:focus-visible {
    color: var(--text-primary);
    border-color: var(--text-secondary);
    outline: none;
}
.help-icon[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    left: 0;
    bottom: calc(100% + 8px);
    width: max-content;
    max-width: 280px;
    padding: 8px 10px;
    border-radius: var(--radius);
    background: var(--text-primary);
    color: var(--bg-primary);
    font-size: 0.8rem;
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    white-space: normal;
    box-shadow: var(--shadow);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition);
    z-index: 20;
    pointer-events: none;
}
.help-icon:hover::after,
.help-icon:focus-visible::after {
    opacity: 1;
    visibility: visible;
}

/* Sprint Stats */
.sprint-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 0;
}

.sprint-stats .stat-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: 500;
    background: var(--surface-sunken);
    color: var(--text-secondary);
}

.sprint-stats .stat-badge.total {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
}

.sprint-stats .stat-badge.done {
    background: var(--success);
    color: #fff;
}

/* Sprint Progress */
.sprint-progress {
    padding: 16px;
}

.sprint-progress .progress-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border-color);
}

.sprint-progress .progress-item:last-child {
    border-bottom: none;
}

.sprint-progress .progress-item .status-icon {
    width: 24px;
    text-align: center;
    font-size: 1.1rem;
}

.sprint-progress .progress-item.pending .status-icon {
    color: var(--text-muted);
}

.sprint-progress .progress-item.running .status-icon {
    color: var(--accent-text);
}

.sprint-progress .progress-item.success .status-icon {
    color: var(--success);
}

.sprint-progress .progress-item.error .status-icon {
    color: var(--danger);
}

/* Sprint Progress - Interactive */
.progress-project {
    border: 1px solid var(--border-color);
    border-radius: 8px;
    margin-bottom: 10px;
    overflow: hidden;
    transition: border-color 0.3s;
}

.progress-project.done {
    border-color: var(--success, #28a745);
}

.progress-project.failed {
    border-color: var(--danger, #dc3545);
}

/* Neutral "still advancing" state for hasDevelop releases tracking a PR merge +
   post-merge build — not done, not failed. */
.progress-project.tracking {
    border-color: var(--accent-text, #3b82f6);
}

.progress-project-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    background: var(--bg-tertiary);
    font-size: 0.95rem;
}

.progress-project-icon {
    font-size: 1.1rem;
    color: var(--text-muted);
    width: 20px;
    text-align: center;
}

.progress-project-version {
    margin-left: auto;
    font-size: 0.8rem;
    color: var(--text-muted);
    font-family: var(--font-mono);
}

.progress-steps {
    padding: 4px 16px 8px 46px;
}

.progress-step {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 0;
    font-size: 0.85rem;
    color: var(--text-secondary);
    transition: color 0.2s;
}

.progress-step .step-icon {
    width: 18px;
    text-align: center;
    font-size: 1rem;
}

.progress-step.pending .step-icon {
    color: var(--text-muted);
}

.progress-step.active .step-icon {
    color: var(--accent-text);
    animation: spin 1s linear infinite;
}

.progress-step.success {
    color: var(--success, #28a745);
}

.progress-step.error {
    color: var(--danger, #dc3545);
}

/* Skipped/cancelled after an earlier failure — dim it so the real failed
   step (red) is the one that draws the eye. */
.progress-step.skipped {
    color: var(--text-muted);
}

.progress-step.skipped .step-icon {
    color: var(--text-muted);
}

/* Result callout in the build modal — outcome + what to do next. Full border +
   tint (no side-stripe), status-keyed title, neutral body. The base is neutral;
   the --error / --success modifiers key the border + title colour. */
.build-hint {
    margin-top: 14px;
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    background: var(--surface-sunken);
    animation: build-hint-in 250ms cubic-bezier(0.4, 0, 0.2, 1);
}

.build-hint--error {
    border-color: var(--danger, #dc3545);
}

.build-hint--success {
    border-color: var(--success, #28a745);
}

/* Warning callout — a filled yellow tint (not the neutral sunken grey) so an
   actionable warning like a merge conflict catches the eye instead of reading
   as a quiet note. Mirrors the .finalize-warning treatment. */
.build-hint--warn {
    border-color: var(--warning);
    background: var(--warning-bg);
}

.build-hint--warn .build-hint__title {
    color: var(--warning);
}

.build-hint__title {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--text-primary);
}

.build-hint__icon {
    flex-shrink: 0;
    font-size: 1rem;
    line-height: 1;
}

/* CTA chip inside a callout — a real, solid button from the start (not a bare
   link that only looks like a button on hover). Variant-keyed solid fill with a
   trailing ↗ glyph; hover only deepens the fill + lifts. */
.build-hint__action {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 12px;
    padding: 8px 15px;
    border-radius: var(--radius);
    border: 1px solid transparent;
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    box-shadow: 0 1px 2px hsl(0 0% 0% / 0.12);
    transition: background var(--transition), color var(--transition),
                box-shadow var(--transition), transform var(--press);
}

/* Secondary action beside the CTA chip (e.g. "Re-check"). It carries no margin
   of its own, so without this it sits flush against the chip and the two read as
   one split control. Same 12px top margin so both sit on one baseline. */
.build-hint__recheck {
    margin-top: 12px;
    margin-left: 8px;
    vertical-align: top;
}

.build-hint__action::after {
    content: "\2197";          /* ↗ opens on GitHub */
    font-size: 0.95em;
    line-height: 1;
}

.build-hint__action:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
    text-decoration: none;
}

.build-hint__action:active {
    transform: scale(0.97);
}

/* Amber fill with dark ink (AA on the saturated warning), per the Bootstrap
   btn-warning convention — reads as a warning action, not a generic blue link. */
.build-hint__action--warn {
    background: color-mix(in srgb, var(--warning), #fff 14%);
    color: #241700;
}

.build-hint__action--warn:hover {
    background: var(--warning);
    color: #241700;
}

.build-hint__action--error {
    background: var(--danger);
    color: #fff;
}

.build-hint__action--error:hover {
    background: var(--danger-hover);
    color: #fff;
}

.build-hint__action:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .build-hint__action { transition: none; }
    .build-hint__action:hover { transform: none; }
}

.build-hint--error .build-hint__title {
    color: var(--danger, #dc3545);
}

.build-hint--success .build-hint__title {
    color: var(--success, #28a745);
}

@keyframes build-hint-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .build-hint { animation: none; }
}

/* Version-collision hint under the Release version field — explains why the
   prefilled number is taken and offers the next free one. Warning-keyed, not an
   error: the field stays usable. */
.version-warn {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 6px;
    padding: 8px 10px;
    border: 1px solid var(--warning);
    border-radius: var(--radius);
    background: var(--surface-sunken);
    font-size: 0.8rem;
    color: var(--text-primary);
}

.version-warn__icon { color: var(--warning); flex-shrink: 0; }
.version-warn__text { flex: 1; min-width: 220px; line-height: 1.4; }
.version-warn__use { margin-left: auto; flex-shrink: 0; }

.build-hint__step {
    margin-top: 3px;
    font-size: 0.8rem;
    color: var(--text-secondary);
    font-family: var(--font-mono);
}

.build-hint__body {
    margin: 8px 0 0;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--text-primary);
}

/* Status cell: truncating text + an optional "Open" link that must NOT be
   truncated away. Clipping happens on the text span, not the cell, so the link
   survives a long message instead of being the first thing cut off. */
.progress-step .step-status {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    margin-left: auto;
    min-width: 0;
    max-width: 55%;
    font-size: 0.75rem;
    color: var(--text-muted);
}

.step-status__text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.step-status__link {
    flex-shrink: 0;
    color: var(--accent-text);
    text-decoration: none;
    font-weight: 600;
}

.step-status__link::after {
    content: "\2197";          /* ↗ opens on GitHub */
    margin-left: 3px;
}

.step-status__link:hover {
    text-decoration: underline;
}

/* A step that will NOT run. It stays in flow order rather than disappearing —
   an absent row reads as "forgotten", which is the opposite of the truth. Held
   quieter than a pending step (dimmed label, en-dash marker) so the eye still
   travels down the steps that DO run, and tagged in words, never by dimming
   alone. */
.progress-step.skipped { color: var(--text-muted); }
.progress-step.skipped .step-icon { color: var(--text-muted); opacity: 0.7; }
.progress-step.skipped .step-label { opacity: 0.75; }

.progress-step.skipped .step-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 320px;
}

.step-skip-tag {
    padding: 1px 7px;
    border: 1px dashed var(--border-strong);
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.loading-spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
    vertical-align: middle;
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Changelog Preview */
.changelog-preview {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.cl-toolbar {
    display: flex;
    justify-content: flex-end;
    padding: 8px 0;
}

.cl-view-toggle {
    display: flex;
    gap: 2px;
    background: var(--surface-sunken);
    border-radius: 6px;
    padding: 2px;
}

.cl-view-toggle .btn { font-size: 0.8rem; }
.cl-view-toggle .btn.active {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
}

.cl-type-tabs, .cl-project-tabs {
    display: flex;
    gap: 4px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border-color);
}

.cl-type-tabs .btn.active, .cl-project-tabs .btn.active {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
}

.cl-content {
    padding: 16px 0;
    min-height: 300px;
    max-height: 500px;
    overflow-y: auto;
    font-size: 0.9rem;
    line-height: 1.6;
}

.cl-content h2 { font-size: 1.1rem; margin: 16px 0 8px; }
.cl-content h3 { font-size: 1rem; margin: 12px 0 6px; }
.cl-content ul { margin: 4px 0; padding-left: 20px; }
.cl-content a { color: var(--accent-text); }

.cl-editor {
    width: 100%;
    min-height: 300px;
    max-height: 500px;
    padding: 12px;
    font-family: var(--font-mono);
    font-size: 0.85rem;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    background: var(--bg-primary);
    color: var(--text-primary);
    resize: vertical;
    box-sizing: border-box;
}

/* Email Review */
.email-review-content {
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 16px;
    min-height: 200px;
    max-height: 400px;
    overflow-y: auto;
    background: var(--bg-secondary);
    font-size: 0.9rem;
    line-height: 1.6;
}

.email-review-content:focus {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

/* Skip Steps */
.skip-steps {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    margin-top: 8px;
    border-top: 1px solid var(--border-color);
    font-size: 0.85rem;
}

.skip-label {
    color: var(--text-muted);
    font-weight: 500;
}

.skip-option {
    display: flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    color: var(--text-secondary);
}

.skip-option input[type="checkbox"] {
    margin: 0;
}

/* "Pushes: v7.1.1-b0.0.1" sits in the same centered flex row as the checkbox;
   .hint's 12px bottom margin is part of the centered box, so it rides the text
   up out of line with the label. */
.sprint-taglatest-hint {
    margin: 0;
}

/* ============================================================================
   Release History — operations console cards
   A calm card per release: identity + ONE semantic status pill up top, a
   progress track for "where you are", per-project status, and an action row
   where the in-flight primary (Resume) carries the accent while Revert stays a
   quiet danger ghost (One Voice + Danger-Is-Distinct).
   ========================================================================== */

/* "N releases awaiting announcement" — an accent CTA, not a muted chip. */
.rel-awaiting {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
    padding: 8px 14px;
    border-radius: 999px;
    border: 1px solid var(--accent-text);
    background: var(--accent-tint);
    color: var(--accent-text);
    font-size: 0.82rem;
    font-weight: 600;
}

.rel-awaiting__dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent-text);
    box-shadow: 0 0 0 0 var(--accent-text);
    animation: rel-pulse 2s var(--ease-out) infinite;
}

@keyframes rel-pulse {
    0%   { box-shadow: 0 0 0 0 hsl(var(--accent) / 0.5); }
    70%  { box-shadow: 0 0 0 6px hsl(var(--accent) / 0); }
    100% { box-shadow: 0 0 0 0 hsl(var(--accent) / 0); }
}

.release-history-item.release-card {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    background: var(--surface);
    /* A resting shadow (not only on hover) lifts each card off the panel so two
       releases never blur into one another. */
    box-shadow: var(--shadow);
    margin-bottom: 16px;
    padding: 16px 18px;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.release-history-item.release-card:hover {
    border-color: hsl(var(--accent) / 0.4);
    box-shadow: var(--shadow-md);
}

.release-card__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.release-card__title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

.release-card__name {
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-primary);
}

.release-card__meta {
    margin-top: 4px;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.release-card__actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

/* Per-project release outcome rows (terminal releases). */
.release-card__projects {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.rel-proj__ver {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    padding: 1px 7px;
    border-radius: 6px;
    background: var(--surface-sunken);
    color: var(--text-secondary);
}

/* ── Semantic status pill — icon + label, tone-keyed (never colour alone). ── */
.rel-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}

.rel-pill__icon { font-size: 0.85em; line-height: 1; }

.rel-pill--success { background: var(--status-success-bg-pill, hsl(var(--status-success-bg))); color: var(--success); }
.rel-pill--warn    { background: hsl(var(--status-warning-bg)); color: var(--warning); }
.rel-pill--danger  { background: hsl(var(--status-error-bg));   color: var(--danger); }
.rel-pill--info    { background: hsl(var(--status-info-bg));    color: var(--accent-text); }
.rel-pill--muted   { background: var(--surface-sunken);         color: var(--text-secondary); }
/* 'ready-to-announce' — the actionable success state, carries the accent. */
.rel-pill--ready   { background: var(--accent-tint); color: var(--accent-text); }

/* The 'Building' gear spins; the in-progress dot just sits (no false motion). */
.rel-pill--info .rel-pill__icon { display: inline-block; }

/* ── Live sync block: progress + per-project rows + Resume ── */
.release-sync-status { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-color); }
.rel-checking { margin: 0; }

.rel-progress {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}

.rel-progress__track {
    position: relative;
    flex: 1;
    height: 8px;
    border-radius: 999px;
    background: var(--surface-sunken);
    overflow: hidden;
}

.rel-progress__fill {
    height: 100%;
    width: 100%;
    border-radius: 999px;
    background: var(--accent-primary);
    transform-origin: left center;
    transform: scaleX(0);
    transition: transform 0.5s var(--ease-out);
}

.rel-progress--ready  .rel-progress__fill { background: var(--success); }
.rel-progress--blocked .rel-progress__fill { background: var(--warning); }

.rel-progress__label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.rel-sync-projects {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.rel-sync-proj {
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.rel-sync-row {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 0.85rem;
}

/* ── Stage stepper: Changelog → Merge PR → Build → Released ──
   A row of equal segments; each carries a bar (state colour) + a label.
   Shows which stage the process is on and where it's blocked. */
.rel-stages {
    display: flex;
    gap: 6px;
}

.rel-stage {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.rel-stage__bar {
    height: 4px;
    border-radius: 999px;
    background: var(--surface-sunken);
}

.rel-stage__label {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rel-stage--done .rel-stage__bar    { background: var(--success); }
.rel-stage--done .rel-stage__label  { color: var(--text-secondary); }

.rel-stage--current .rel-stage__bar    { background: var(--accent-primary); animation: rel-stage-glow 1.8s var(--ease-out) infinite; }
.rel-stage--current .rel-stage__label  { color: var(--accent-text); }

.rel-stage--blocked .rel-stage__bar    { background: var(--warning); }
.rel-stage--blocked .rel-stage__label  { color: var(--warning); }

.rel-stage--skipped .rel-stage__bar   { background: var(--surface-sunken); border: 1px dashed var(--border-color); height: 4px; box-sizing: border-box; }
.rel-stage--skipped .rel-stage__label { color: var(--text-muted); font-weight: 500; }

@keyframes rel-stage-glow {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}

/* ── Per-project step detail — the steps behind the stage groups, collapsed by
   default so a list of releases stays scannable. */
.rel-steps {
    font-size: 0.75rem;
}

.rel-steps__summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--text-muted);
    font-weight: 600;
    padding: 2px 0;
    transition: color var(--transition);
}

.rel-steps__summary::-webkit-details-marker { display: none; }

.rel-steps__summary::before {
    content: "\25B8";                      /* ▸ — rotates to ▾ when open */
    font-size: 0.7em;
    transition: transform var(--transition);
}

.rel-steps[open] > .rel-steps__summary::before { transform: rotate(90deg); }
.rel-steps__summary:hover { color: var(--text-secondary); }
.rel-steps__summary:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; border-radius: var(--radius); }

.rel-steps__list {
    list-style: none;
    margin: 6px 0 2px;
    padding: 0 0 0 2px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.rel-step {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
}

.rel-step__icon {
    flex: 0 0 auto;
    width: 12px;
    text-align: center;
    line-height: 1;
    color: var(--text-muted);
}

.rel-step__label { color: var(--text-secondary); }

.rel-step__msg,
.rel-step__link {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: 0.95em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rel-step__link { color: var(--accent-text); }
.rel-step__link:hover { text-decoration: underline; }

.rel-step--done .rel-step__icon    { color: var(--success); }
.rel-step--failed .rel-step__icon  { color: var(--danger); }
.rel-step--failed .rel-step__label { color: var(--danger); }
.rel-step--running .rel-step__icon { color: var(--accent-primary); }
.rel-step--pending .rel-step__label,
.rel-step--skipped .rel-step__label { color: var(--text-muted); }

.rel-sync-row__name {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* PR chip — a real link affordance with a trailing ↗, not a bare "PR". */
.rel-pr-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    padding: 3px 10px;
    border-radius: var(--radius);
    border: 1px solid var(--accent-text);
    color: var(--accent-text);
    font-size: 0.75rem;
    font-weight: 600;
    text-decoration: none;
    transition: background var(--transition), color var(--transition), transform var(--press);
}

.rel-pr-chip::after { content: "\2197"; font-size: 0.95em; line-height: 1; }

/* Waived-PR chip — a deliberate decision, so it reads as a stated fact rather
   than a missing step. Muted (not a link, nothing to click). */
.rel-skip-chip {
    display: inline-flex;
    align-items: center;
    margin-left: auto;
    padding: 3px 10px;
    border-radius: var(--radius);
    border: 1px dashed var(--border-color);
    color: var(--text-secondary);
    font-size: 0.75rem;
    font-weight: 600;
}

.rel-pr-chip:hover { background: var(--accent-tint); }
.rel-pr-chip:active { transform: scale(0.97); }
.rel-pr-chip:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }

/* Blocked-state callout — explains why the release stopped and what to do
   next, so Resume never reads as a no-op. Filled warning tint to catch the eye. */
.rel-callout {
    margin-top: 14px;
    padding: 12px 14px;
    border-radius: var(--radius);
    border: 1px solid var(--warning);
    background: var(--warning-bg);
}

.rel-callout__title {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--warning);
}

.rel-callout__body {
    margin: 7px 0 0;
    font-size: 0.83rem;
    line-height: 1.5;
    color: var(--text-primary);
}

/* The actual error from the failed step — what's wrong, in the operator's
   words. Mono for the step name + message; quiet, on a faint inset. */
.rel-callout__detail {
    margin: 9px 0 0;
    padding: 7px 10px;
    border-radius: var(--radius);
    background: hsl(var(--foreground) / 0.04);
    font-family: var(--font-mono);
    font-size: 0.76rem;
    line-height: 1.5;
    color: var(--text-secondary);
    word-break: break-word;
}

.rel-callout__detail strong { color: var(--text-primary); font-weight: 700; }

.rel-sync-actions {
    margin-top: 18px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

/* One line under the action row naming what the primary action actually does —
   an operator mid-release should not have to guess whether a button re-checks
   or continues. Quiet: it explains, it never competes with the buttons. */
.rel-actions-hint {
    margin: 8px 0 0;
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--text-muted);
    max-width: 68ch;
}

/* "Resolve on GitHub" CTA carries the same trailing ↗ as the PR chip. */
.rel-resolve { text-decoration: none; }
.rel-resolve::after { content: "\2197"; font-size: 0.95em; line-height: 1; margin-left: 2px; }

/* Build-failure context inside the blocked callout — a direct link to the
   failed run and a plain-language hint. The link reads as an inline accent
   affordance (same trailing ↗ vocabulary as the PR chip); the hint is quiet
   secondary text that explains what to check before deciding. */
.rel-callout__runlink {
    display: inline-flex;
    align-items: center;
    margin-top: 9px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--accent-text);
    text-decoration: none;
    border-radius: var(--radius);
}
.rel-callout__runlink::after {
    content: "\2197";
    font-size: 0.95em;
    line-height: 1;
    margin-left: 3px;
}
.rel-callout__runlink:hover { text-decoration: underline; }
.rel-callout__runlink:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}

.rel-callout__hint {
    margin: 8px 0 0;
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-secondary);
}
.rel-callout__hint strong { color: var(--text-primary); font-weight: 700; }

/* Skip-verification modal — a stacked, readable choice row: radio + label,
   the whole row a pointer target. 8px-grid rhythm, faint hover inset so the
   choice feels clickable; focus shows on the radio itself. */
.radio-group { margin: 8px 0 4px; }
.radio-row {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: var(--radius);
    font-size: 0.85rem;
    line-height: 1.45;
    color: var(--text-primary);
    cursor: pointer;
}
.radio-row:hover { background: hsl(var(--foreground) / 0.04); }
.radio-row input[type="radio"] {
    margin: 2px 0 0;
    flex: 0 0 auto;
    cursor: pointer;
    accent-color: var(--accent-text);
}
.radio-row input[type="radio"]:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
}
.radio-row span { flex: 1 1 auto; }

@media (prefers-reduced-motion: reduce) {
    .rel-awaiting__dot { animation: none; }
    .rel-progress__fill { transition: none; }
    .rel-stage--current .rel-stage__bar { animation: none; }
    .rel-pr-chip { transition: none; }
    .release-history-item.release-card { transition: none; }
}

/* Cleanup Sub-tabs */
.cleanup-subtabs {
    display: flex;
    gap: 4px;
    margin-bottom: 16px;
}

.cleanup-subtab {
    padding: 8px 16px;
    background: var(--surface-interactive);
    border: none;
    border-radius: var(--radius);
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}

.cleanup-subtab:hover {
    background: var(--surface-interactive-hover);
}

.cleanup-subtab.active {
    background: var(--accent-tint);
    color: var(--accent-text);
    font-weight: 600;
    box-shadow: inset 0 0 0 1px var(--accent-text);
}

.cleanup-tab-content {
    display: none;
}

.cleanup-tab-content.active {
    display: block;
}

/* Cleanup Actions Bar */
.cleanup-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 16px;
    padding: 12px 16px;
    background: var(--bg-tertiary);
    border-radius: var(--radius);
}

.cleanup-actions-btns {
    display: flex;
    gap: 8px;
}

.cleanup-summary {
    font-size: 0.85rem;
    color: var(--text-secondary);
}

/* QA History collapsible panel */
.history-panel > .history-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    cursor: pointer;
    list-style: none;
    font-weight: 600;
    padding: 4px 0;
}
.history-panel > .history-summary::-webkit-details-marker { display: none; }
.history-panel > .history-summary::before {
    content: '▶';
    display: inline-block;
    margin-right: 8px;
    transition: transform 0.15s;
    font-size: 0.8rem;
    color: var(--text-secondary);
}
.history-panel[open] > .history-summary::before { transform: rotate(90deg); }
.history-body { margin-top: 12px; overflow-x: auto; }
.history-body .data-table td { vertical-align: top; }
.history-body code {
    font-family: var(--font-mono);
    font-size: 0.8rem;
}
.qa-status-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.75rem;
    font-weight: 600;
}
/* Status badges read from the AA-tuned status triplets, which retheme
   themselves under [data-theme="dark"] — no per-theme text overrides needed. */
.qa-status-badge.success { background: hsl(var(--status-success-bg)); color: hsl(var(--status-success-text)); }
.qa-status-badge.failed  { background: hsl(var(--status-error-bg));   color: hsl(var(--status-error-text)); }

/* Cleanup Type Badges */
.cleanup-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.tag-release {
    background: hsl(var(--status-success-bg));
    color: hsl(var(--status-success-text));
}

.tag-branch {
    background: var(--accent-tint);
    color: var(--accent-text);
}

.branch-protected {
    background: hsl(var(--status-error-bg));
    color: hsl(var(--status-error-text));
}

.branch-merged {
    background: hsl(var(--status-success-bg));
    color: hsl(var(--status-success-text));
}

.branch-stale {
    background: hsl(var(--status-warning-bg));
    color: hsl(var(--status-warning-text));
}

.branch-active {
    background: var(--surface-sunken);
    color: var(--text-secondary);
}

.pull-never {
    color: var(--danger);
    font-weight: 500;
    font-size: 0.85rem;
}

/* Sortable Table Headers */
.cleanup-table th.sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.cleanup-table th.sortable:hover {
    color: var(--accent-text);
}

.sort-icon {
    font-size: 0.7rem;
    margin-left: 2px;
    opacity: 0.4;
}

.sort-icon.asc::after { content: '\25B2'; opacity: 1; }
.sort-icon.desc::after { content: '\25BC'; opacity: 1; }

/* Scheduled Info */
.scheduled-info {
    margin-bottom: 16px;
    padding: 12px 16px;
    background: var(--bg-tertiary);
    border-radius: var(--radius);
}

.scheduled-info p {
    margin: 4px 0;
    font-size: 0.9rem;
}

#scheduledContent h4 {
    margin: 16px 0 8px;
    color: var(--text-primary);
}

.row-excluded td {
    opacity: 0.5;
}

/* Cleanup Filter Bar */
.cleanup-filter-bar {
    display: flex;
    gap: 12px;
    align-items: center;
    margin-bottom: 12px;
}

/* Responsive */
@media (max-width: 768px) {
    .app {
        padding: 12px;
    }

    .header {
        padding: 12px 16px;
    }

    .logo {
        font-size: 1.2rem;
    }

    .tabs {
        flex-wrap: wrap;
    }

    .tab {
        flex: 1 1 auto;
        padding: 10px 16px;
        font-size: 0.85rem;
    }

    .card {
        padding: 16px;
    }

    .form-row {
        grid-template-columns: 1fr;
    }

    .data-table {
        font-size: 0.8rem;
    }

    .data-table th,
    .data-table td {
        padding: 8px;
    }
}

/* ============================================================
   Reduced motion. Operators mid-incident may have this set system-wide;
   honour it (PRODUCT.md a11y target). Movement and looping animation
   collapse to near-instant; opacity/colour changes that aid comprehension
   still resolve. JS smooth-scroll is already gated in wizard.js.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* Regenerate-changelog preview: the per-branch "what would change" line above
   the rendered pages. Sits on the sunken surface so it reads as a summary of
   the pages below, not as another page. */
.regen-branch-summary {
    padding: 10px 12px;
    margin-bottom: 12px;
    background: var(--surface-sunken);
    border: 1px solid var(--border-color);
    border-radius: var(--radius);
    color: var(--text-secondary);
    font-size: 0.9rem;
}

/* Build-hint actions row: the PR link and its Re-check button sit side by side,
   so "look at it" and "I merged it, check again" read as one pair — which means
   they must look like a pair. The base .build-hint__action sets shape but no
   fill (its --warn/--error variants supply that), so a neutral fill is given
   here; without it the <a> took the browser's link colour and the <button> its
   default chrome, and the two read as different kinds of thing. */
.build-hint__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
}

.build-hint__actions .build-hint__action {
    margin-top: 0;
    background: var(--surface-interactive);
    color: var(--text-primary);
    /* Pinned so the link's ↗ pseudo-element cannot make it taller than the
       button beside it — a 3px mismatch reads as sloppy alignment. */
    line-height: 1.25;
}

.build-hint__actions .build-hint__action:hover {
    background: var(--surface-interactive-hover);
    color: var(--text-primary);
}

button.build-hint__action {
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 600;
}

/* The ↗ says "opens on GitHub" — a button stays on the page. */
button.build-hint__action::after {
    content: none;
}

button.build-hint__action:disabled {
    opacity: 0.7;
    cursor: progress;
    transform: none;
    box-shadow: none;
}

/* The docs-portal toggle follows the project list, which ends flush against it
   — without this it reads as part of the last project row. */
.regen-docs-toggle {
    margin-top: 20px;
}

/* Release New Build batch picker: a compact operational table, not a second
   card system. Each service exposes its independent branch and local result. */
/* A <fieldset> purely for grouping semantics — named by the step's own heading,
   with no legend and no box. Finalize Sprint's list has no wrapper at all, and
   an outlined panel around it was a second frame the sprint does not draw. */
.rb-batch-picker {
    margin: 0 0 14px;
    padding: 0;
    border: 0;
    min-width: 0;
}

/* The picker IS Finalize Sprint's Select Projects: `.project-checkbox-list`
   owns the grid, the group headers and the row treatment, and both screens read
   from that one place. Nothing service-specific is added here — a second,
   parallel set of choice classes is what let the two screens drift apart. */

/* A selected service configures itself in place, INSIDE its own grid cell: the
   pill keeps the width and the position it had unselected, and the branch
   control opens underneath it. Widening the selection to the whole row made the
   picked service jump the layout, which is the one row the operator is looking
   at. `align-items: start` on the list is what keeps the taller cell from
   stretching its neighbours on the same line. */
.project-choice { min-width: 0; }

/* The SELECTED card is the wrapper, not the pill: the branch control lives
   INSIDE the same box as the service it configures. The pill inside it goes
   transparent so the box is drawn once — two nested rounded fills read as two
   controls. */
.project-choice--selected {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-bottom: 10px;
    background: var(--accent-tint);
    box-shadow: inset 0 0 0 1px var(--accent-text);
    border-radius: var(--radius);
}

/* `:has(input:checked)` carries its argument's specificity, so the pill's own
   checked rule out-ranks a plain descendant selector — these have to match it
   or the card is drawn twice, one ring inside the other. */
.project-checkbox-list .project-choice--selected .project-checkbox-item,
.project-checkbox-list .project-choice--selected .project-checkbox-item:hover,
.project-checkbox-list .project-choice--selected .project-checkbox-item:has(input:checked),
.project-checkbox-list .project-choice--selected .project-checkbox-item:has(input:checked):hover {
    background: transparent;
    box-shadow: none;
}

.project-choice__branch {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 0 12px;
    min-width: 0;
}

.project-choice__branch-label {
    color: var(--text-secondary);
    font-size: 0.85rem;
    font-weight: 500;
    white-space: nowrap;
}

.project-choice__branch select { width: 100%; min-width: 0; }

/* The trigger's default fill is `--bg-primary`, the RECESSED input tone — read
   against the card's own tint it is one grey on one blue and the control stops
   looking like a control. On a tinted card it takes the card tone (`--bg-secondary`)
   and the stronger border, so the one thing on this card that must be operated
   is the one thing that stands out. */
.project-choice--selected .ui-select-trigger,
.project-choice--selected .project-choice__branch select {
    background: var(--bg-secondary);
    border-color: var(--border-strong);
}

/* The per-row state that used to sit in the separate table. NO flex-basis: the
   branch block is a COLUMN, so a basis is read as a height and a one-line hint
   reserved 200px of empty card. */
.project-choice__state { min-width: 0; }

.rb-batch-table { display: grid; gap: 8px; }
.rb-batch-row {
    display: grid;
    grid-template-columns: minmax(180px, 1.3fr) minmax(180px, 1fr) minmax(180px, 1fr);
    align-items: end;
    gap: 12px;
    padding: 10px 12px;
    border-top: 1px solid var(--border-color);
}
.rb-batch-row:first-child { border-top: 0; }
/* The Configuration step's row: the same table, one column wider — read-only
   version, the operator's build, its own `latest`, and the resulting tag. */
.rb-batch-row--config {
    grid-template-columns: minmax(160px, 1.4fr) minmax(90px, 0.6fr) minmax(90px, 0.6fr) minmax(200px, 1.2fr);
}

/* `latest` is switched ON the tag it adds, so the control sits beside the
   preview and reads as the second tag rather than as a sentence about one. */
.rb-tag-chip {
    padding: 2px 8px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill, 999px);
    background: transparent;
    color: var(--text-secondary);
    font-family: var(--font-mono, monospace);
    font-size: 0.78rem;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
}

.rb-tag-chip:hover { border-color: var(--border-strong); }

.rb-tag-chip--on {
    background: var(--accent-tint);
    border-color: var(--accent-text);
    color: var(--accent-text);
}

.rb-batch-row__warning-why {
    color: var(--text-secondary);
    font-size: 0.85rem;
}
.rb-batch-row--config .checkbox-group { align-self: end; }

/* The service's Jira tickets sit on their own full-width line under its
   controls: the row's five tracks are sized for form fields, and a ticket list
   is as long as the release happens to be. */
.rb-batch-row__jira {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.rb-batch-row__jira-label {
    color: var(--text-secondary);
    font-size: 0.85rem;
    font-weight: 500;
}

/* Same full-width line as the ticket list, and the same reason. */
.rb-batch-row__warning {
    grid-column: 1 / -1;
    margin-top: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}
.rb-batch-row .form-group { margin: 0; }
.rb-batch-row__result { min-height: 38px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.rb-batch-row__state { color: var(--text-secondary); font-size: 0.85rem; }
.rb-batch-row__state--ready { color: var(--success); font-weight: 600; }
.rb-batch-row__state--error { color: var(--danger); font-weight: 600; }
.rb-batch-empty { margin: 0; color: var(--text-secondary); font-size: 0.9rem; }

/* The durable batch's own status line — sync failures only. Silent when empty
   so it costs no space on a page that has nothing to report. */
.rb-batch-sync-status:empty { display: none; }
.rb-batch-sync-status--error { color: var(--danger); font-weight: 600; }

/* The release notification's verdict and its escape hatch. */
.rb-batch-notify__to {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    margin: 8px 0;
}
.rb-batch-notify__to label { color: var(--text-secondary); font-size: 0.85rem; font-weight: 500; }
/* A recipients list is a short field, not a full-width slab. */
.rb-batch-notify__to input { flex: 1 1 260px; max-width: 420px; min-width: 0; }
.rb-batch-notify__to .hint { flex: 1 1 100%; margin: 0; }

/* The publication verdict + Resume, under a batch card's service rows. */
.rb-batch-card__controls {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border-color);
}
.rb-batch-card__controls .build-hint { margin: 0 0 8px; }
.rb-batch-card__controls > *:last-child { margin-bottom: 0; }
/* Inside a card the rows are already closed by the controls' own rule. */
.rb-batch-card .release-card__projects.rb-batch-progress { margin-bottom: 0; }

/* The per-service rows have two hosts: the submit modal in session, and the
   batch card in Release Build History after a reload. Same rows, same
   treatment. */
.rb-batch-monitor { display: grid; gap: 14px; }

/* The batch in one line: how many are done, how far it has moved, what is
   holding it up. Counting rows by eye is not an operator's job. */
.rb-batch-summary {
    display: grid;
    gap: 8px;
    padding: 14px 16px;
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--text-muted);
    border-radius: var(--radius-md, 8px);
    background: var(--surface-sunken);
}
.rb-batch-summary--info    { border-left-color: var(--accent-text); }
.rb-batch-summary--success { border-left-color: var(--success); }
.rb-batch-summary--warn    { border-left-color: var(--warning); }
.rb-batch-summary--danger  { border-left-color: var(--danger); }
.rb-batch-summary__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
.rb-batch-summary__count { font-weight: 600; font-size: 0.98rem; }
.rb-batch-summary__bar {
    height: 6px;
    border-radius: 999px;
    background: var(--border-color);
    overflow: hidden;
}
.rb-batch-summary__bar > span {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--accent-text);
    transition: width 320ms var(--ease-out);
}
.rb-batch-summary--success .rb-batch-summary__bar > span { background: var(--success); }
.rb-batch-summary--danger .rb-batch-summary__bar > span { background: var(--danger); }
.rb-batch-summary--warn .rb-batch-summary__bar > span { background: var(--warning); }
.rb-batch-summary__note { margin: 0; color: var(--text-secondary); font-size: 0.85rem; }

#rbBatchProgress,
.rb-batch-progress { display: grid; gap: 10px; }
.rb-batch-progress:not(:empty) { margin-bottom: 14px; }

/* A service is a CARD, not a table line: it carries a name, a version, a
   status, a four-stage track and its links, and a borderless row made those
   five things read as one run-on sentence. */
.rb-batch-progress-row {
    display: grid;
    gap: 10px;
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--text-muted);
    border-radius: var(--radius-md, 8px);
    background: var(--bg-primary, var(--surface-sunken));
}
.rb-batch-progress-row--preparing-changelog,
.rb-batch-progress-row--changelog-pr-merged,
.rb-batch-progress-row--committed,
.rb-batch-progress-row--dispatched,
.rb-batch-progress-row--building { border-left-color: var(--accent-text); }
.rb-batch-progress-row--changelog-pr-open { border-left-color: var(--warning); }
.rb-batch-progress-row--changelog-pr-conflict,
.rb-batch-progress-row--failed { border-left-color: var(--danger); }
.rb-batch-progress-row--image-ready { border-left-color: var(--success); }

.rb-batch-progress-row__head {
    display: flex;
    align-items: center;
    gap: 8px 12px;
    flex-wrap: wrap;
}
/* Name over repository: the operator picked "ACS", the record keys by
   repository, and only one of those two is what they chose. */
.rb-batch-progress-row__id {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    margin-right: auto;
}
.rb-batch-progress-row__repo {
    color: var(--text-secondary);
    font-size: 0.78rem;
    overflow-wrap: anywhere;
}
.rb-batch-progress-row__version {
    font-size: 0.78rem;
    color: var(--text-secondary);
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--surface-sunken);
    white-space: nowrap;
}
.rb-batch-progress-row .rel-pill { justify-self: start; }
/* The spinner inherits the pill's own tone rather than the accent, or an
   in-flight row would carry two different colours for one state. */
.rb-batch-progress-row .rel-pill .loading-spinner {
    width: 11px;
    height: 11px;
    border-width: 2px;
    border-top-color: currentColor;
}
.rb-batch-progress-row__links { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.rb-batch-progress-row__image {
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 0.76rem;
    color: var(--text-secondary);
    overflow-wrap: anywhere;
    margin-right: auto;
}
.rb-batch-progress-row .btn { white-space: nowrap; }

/* The four stages, always all four. "Building image" answers where a service is
   only for somebody who already knows the sequence; the track states it. */
.rb-stage-track {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0;
    margin: 2px 0 0;
    padding: 0;
    list-style: none;
}
.rb-stage {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    min-width: 0;
    color: var(--text-muted);
    font-size: 0.72rem;
    text-align: center;
}
/* The connector belongs to the stage on its right, so the first one has none
   and the track never ends in a dangling line. */
.rb-stage + .rb-stage::before {
    content: "";
    position: absolute;
    top: 9px;
    right: 50%;
    left: -50%;
    height: 2px;
    background: var(--border-color);
}
/* Same specificity as the connector rule itself, or "todo grey" would win on
   every stage that has actually been passed. */
.rb-stage + .rb-stage--done::before,
.rb-stage + .rb-stage--active::before,
.rb-stage + .rb-stage--wait::before,
.rb-stage + .rb-stage--failed::before { background: var(--success); }
.rb-stage__mark {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid var(--border-color);
    background: var(--bg-secondary);
    font-size: 0.7rem;
    line-height: 1;
}
.rb-stage__label { overflow-wrap: anywhere; }
.rb-stage--done .rb-stage__mark {
    border-color: var(--success);
    background: var(--success);
    color: #fff;
}
.rb-stage--done { color: var(--text-secondary); }
.rb-stage--active .rb-stage__mark { border-color: var(--accent-text); background: var(--bg-secondary); }
.rb-stage--active { color: var(--accent-text); font-weight: 600; }
.rb-stage--active .loading-spinner {
    width: 10px;
    height: 10px;
    border-width: 2px;
    border-top-color: var(--accent-text);
}
.rb-stage--wait .rb-stage__mark { border-color: var(--warning); color: var(--warning); }
.rb-stage--wait { color: var(--warning); font-weight: 600; }
.rb-stage--failed .rb-stage__mark {
    border-color: var(--danger);
    background: var(--danger);
    color: #fff;
}
.rb-stage--failed { color: var(--danger); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
    .rb-batch-summary__bar > span { transition: none; }
}

@media (max-width: 768px) {
    .rb-batch-row { grid-template-columns: 1fr; gap: 8px; }
    /* Version and Build are 5-character numbers: stacked full width they read
       as two unrelated forms and push the tag preview off the first screen.
       They stay side by side, and only the service and its tag take a line. */
    .rb-batch-row--config {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 8px 12px;
    }
    .rb-batch-row--config > :first-child,
    .rb-batch-row--config .rb-batch-row__result { grid-column: 1 / -1; }
    .rb-stage__label { font-size: 0.68rem; }
}

/* Review Changelogs — the tickets an empty Project (assets) will drop from the
   External changelog and from Version Control. Sits above the tabs because it
   is about the release, not about the view being read. */
.cl-warn {
    margin-bottom: 14px;
    padding: 12px 14px;
    border-radius: 8px;
    background: var(--warning-bg);
    border: 1px solid hsl(var(--status-warning-border));
}

.cl-warn-title { font-weight: 600; font-size: 14px; color: var(--text-primary); }
.cl-warn-body { margin: 6px 0 0; font-size: 13px; line-height: 1.6; color: var(--text-secondary); }

.cl-warn-list {
    margin: 8px 0 0;
    padding-left: 18px;
    /* A release can carry dozens; the list scrolls rather than pushing the
       changelog itself off the screen. */
    max-height: 8.5rem;
    overflow-y: auto;
    font-size: 13px;
}

.cl-warn-list li { margin-bottom: 3px; }
.cl-warn-summary { color: var(--text-muted); }

/* A hint that belongs to ONE field sits under it, so it is not read as
   guidance for the row of inputs beside it. */
.form-group > .hint { display: block; margin: 4px 0 0; }
