/*
 * Repair — B2 design language stylesheet
 * Phase 35 — hand-written CSS replacing the cdn.tailwindcss.com runtime script.
 *
 * Token reference: see .planning/phases/35-ui-redesign-b2/35-UI-SPEC.md
 *
 * contrast pairs:
 *   (--cs-ink, --cs-card, normal)
 *   (--cs-ink, --cs-paper, normal)
 *   (--cs-ink-soft, --cs-card, normal)
 *   (--cs-ink-soft, --cs-paper, normal)
 *   (--cs-ink-muted, --cs-card, large)
 *   (--cs-green-dark, --cs-card, normal)
 *   (--cs-red, --cs-card, normal)
 *   (--cs-amber-dark, --cs-card, normal)
 *   (--cs-card, --cs-ink, normal)
 *   (--cs-card, --cs-green-dark, normal)
 */

/* ----------------------------------------------------------------------------
   Tokens
---------------------------------------------------------------------------- */
:root {
    --cs-ink: #232228;
    /* 35.4 audit: aligned to prototype --ink2 (#65645f, WCAG 4.88:1 on paper).
       Was #5a5860 (5.76:1 on paper). Both pass AA-normal; proto value chosen
       for visual parity with the locked B2 reference. */
    --cs-ink-soft: #65645f;
    /* 35.4 audit: kept impl #7e7d83 (NOT proto --ink3 #908e85 which is 2.70:1
       and fails AA-normal). This is a deliberate accessibility deviation. */
    --cs-ink-muted: #7e7d83;
    --cs-paper: #ebe9e2;
    --cs-card: #ffffff;
    /* 35.4 audit: --cs-hair kept at #bcbab0 (impl darker). Adding --cs-line
       (#d7d5cc, proto --line) for the lighter interior dividers (metric/host
       column dividers, fieldset/picker borders) that proto uses to separate
       structurally similar elements without dominating the visual hierarchy. */
    --cs-hair: #bcbab0;
    --cs-line: #d7d5cc;
    --cs-hair-strong: #86847b;
    --cs-green: #1f9d6b;
    --cs-green-dark: #147a52;
    --cs-green-soft: #e6f4ee;
    --cs-red: #a83a2d;
    --cs-red-soft: #f7e8e6;
    --cs-amber: #d8961e;
    /* 35.4 audit: aligned to prototype --amber-d #8a5a12 (micro-shade fix). */
    --cs-amber-dark: #8a5a12;
    --cs-amber-soft: #faf2dc;
    --cs-host-gitlab: #fc6d26;
    --cs-host-github: #181717;
    --cs-host-bitbucket: #2684ff;

    --cs-font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --cs-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, Consolas, monospace;

    --cs-space-1: 4px;
    --cs-space-2: 8px;
    --cs-space-3: 12px;
    --cs-space-4: 16px;
    --cs-space-5: 24px;
    --cs-space-6: 32px;
    --cs-space-7: 48px;

    --cs-fs-eyebrow: 11px;
    --cs-fs-small: 12px;
    --cs-fs-body: 14px;
    --cs-fs-default: 16px;
    --cs-fs-subhead: 20px;
    --cs-fs-title: 28px;
    --cs-fs-display: 36px;

    /* 35.4 audit: aligned to prototype proto:28-32 1040px (was 64rem ≈ 1024px). */
    --cs-shell-max: 1040px;
    --cs-hair-width: 1px;
}

/* ----------------------------------------------------------------------------
   @font-face — IBM Plex Sans + Mono (self-hosted)
---------------------------------------------------------------------------- */
@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/static/fonts/IBMPlexSans-Regular.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("/static/fonts/IBMPlexSans-Medium.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("/static/fonts/IBMPlexSans-SemiBold.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("/static/fonts/IBMPlexSans-Bold.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/static/fonts/IBMPlexMono-Regular.woff2") format("woff2");
}
@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("/static/fonts/IBMPlexMono-Medium.woff2") format("woff2");
}

/* ----------------------------------------------------------------------------
   Reset + base
---------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body.b2-page {
    background: var(--cs-paper);
    color: var(--cs-ink);
    font-family: var(--cs-font-sans);
    font-size: var(--cs-fs-default);
    line-height: 1.5;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
img, svg { display: block; max-width: 100%; }
a { color: var(--cs-green-dark); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: 600; line-height: 1.2; color: var(--cs-ink); }
p { margin: 0; }
button { font-family: inherit; }

/* ----------------------------------------------------------------------------
   Typography helpers
---------------------------------------------------------------------------- */
.b2-mono { font-family: var(--cs-font-mono); }
.b2-eyebrow {
    /* 35.4 audit: proto:59-60 `.eyebrow` — letter-spacing 0.11em (was 0.08em),
       margin 0 0 9px 2px (was 0). */
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-eyebrow);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.11em;
    color: var(--cs-ink-soft);
    margin: 0 0 9px 2px;
}
.b2-title {
    /* proto:26 h1: sans 700 26px letter-spacing -.01em (was 28px/600). */
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.01em;
}
.b2-subhead { font-size: var(--cs-fs-subhead); font-weight: 600; }
.b2-body { font-size: var(--cs-fs-body); }
.b2-muted { color: var(--cs-ink-soft); }
.b2-ink-soft { color: var(--cs-ink-soft); }

/* ----------------------------------------------------------------------------
   Layout shell
---------------------------------------------------------------------------- */
.b2-shell {
    /* 35.4 audit: proto:28 `.wrap` — padding 22px 20px 40px (was 24px 32px
       symmetric). Matches the prototype's tighter top padding and asymmetric
       bottom. */
    max-width: var(--cs-shell-max);
    margin-inline: auto;
    padding: 22px 20px 40px;
}
.b2-nav {
    background: var(--cs-card);
    border-bottom: var(--cs-hair-width) solid var(--cs-hair);
}
.b2-nav__inner {
    max-width: var(--cs-shell-max);
    margin-inline: auto;
    padding: 14px 20px; /* proto:32 — exact */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cs-space-4);
    flex-wrap: wrap;
}
.b2-nav__brand {
    /* proto:34-36 .wordmark: sans 700 16px ls -.01em, with 9px gap to mark icon */
    font-family: var(--cs-font-sans);
    font-weight: 700;
    color: var(--cs-ink);
    font-size: 16px;
    letter-spacing: -0.01em;
    display: inline-flex;
    align-items: center;
    gap: 9px;
}
.b2-nav__brand:hover { text-decoration: none; }
.b2-nav__version {
    /* proto:36 .ver — mono 10px ink3 */
    font-family: var(--cs-font-mono);
    font-size: 10px;
    color: var(--cs-ink-muted);
    font-weight: 400;
}
.b2-nav__links {
    /* proto:37-38 .nav — mono 11px uppercase letter-spacing .06em ink2, gap 18px */
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    font-family: var(--cs-font-mono);
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
}
.b2-nav__link {
    font-family: inherit;
    font-size: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: var(--cs-ink-soft);
}
/* R0-1 (phase 35.5): override inherit-all from .b2-nav__links so the user's
   display name keeps its real case. Spec §3 row 109 fixates case = "normal". */
.b2-nav__user {
    font-family: inherit;
    font-size: inherit;
    letter-spacing: inherit;
    text-transform: none;
    color: var(--cs-ink-soft);
}
.b2-nav__link:hover { color: var(--cs-ink); text-decoration: none; }

/* ----------------------------------------------------------------------------
   Card
---------------------------------------------------------------------------- */
.b2-card {
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: var(--cs-space-5);
    margin-bottom: var(--cs-space-4);
}
.b2-card--paused {
    opacity: 0.78;
    border-style: dashed;
    border-color: var(--cs-amber);
}
.b2-card__header {
    /* 35.4 smoke fix (mobile @390px): stack header vertically by default so the
       text column (.b2-card__title + .b2-card__path + .b2-card__summary) gets
       full card width. Previously this was flex-row with `flex-wrap: wrap` and
       the text-column child carried inline `min-width:0; flex:1;` — flexbox
       prioritized shrinking the text column to 0 over wrapping the actions
       block onto a new row, collapsing the summary `<p>` to clientWidth=3 and
       firing the smoke `scrollWidth <= clientWidth + 1` assertion. Restoring
       row-direction at the existing 720px breakpoint (cf. .b2-stat-grid)
       preserves tablet+desktop parity with the prototype (.cs-inner row). */
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--cs-space-4);
}
@media (min-width: 720px) {
    .b2-card__header {
        flex-direction: row;
        align-items: flex-start;
        justify-content: space-between;
        flex-wrap: wrap;
    }
}
.b2-card__title { font-size: var(--cs-fs-subhead); font-weight: 600; margin-bottom: var(--cs-space-1); }
.b2-card__path {
    /* 35.4 smoke fix: `overflow-wrap: anywhere` (not just `word-break: break-all`)
       so the path's flex-item min-content collapses to a single glyph on narrow
       viewports (mobile smoke @ 390px). break-all alone keeps the flex item at
       full content width and overflows. Mirrors .b2-card__summary below. */
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
    color: var(--cs-ink-soft);
    overflow-wrap: anywhere;
    word-break: break-all;
}
.b2-card__summary {
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
    color: var(--cs-ink-soft);
    margin-top: var(--cs-space-2);
    overflow-wrap: anywhere;
}
.b2-card__actions { display: flex; gap: var(--cs-space-2); flex-wrap: wrap; }
.b2-card__divider {
    border: 0;
    border-top: var(--cs-hair-width) solid var(--cs-hair);
    margin: var(--cs-space-4) 0;
}

/* ----------------------------------------------------------------------------
   Buttons
---------------------------------------------------------------------------- */
.b2-btn {
    /* Prototype proto:42-50 `.btn`: mono, 11px, uppercase, letter-spacing .05em,
       padding 8px 13px (35.4 audit: was 8px 16px). */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--cs-space-2);
    padding: 8px 13px; /* proto:45 exact */
    background: var(--cs-card);
    color: var(--cs-ink);
    border: var(--cs-hair-width) solid var(--cs-hair);
    font-family: var(--cs-font-mono);
    font-size: 11px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    cursor: pointer;
    border-radius: 0;
    text-decoration: none;
    transition: background 80ms ease, border-color 80ms ease, color 80ms ease;
}
/* 35.4 audit: proto:46 `.btn:hover` adds a soft paper-tinted background. */
.b2-btn:hover { background: #f3f2ec; border-color: var(--cs-hair-strong); text-decoration: none; }
.b2-btn:focus-visible { outline: 2px solid var(--cs-hair-strong); outline-offset: 2px; }

.b2-btn--primary {
    /* Prototype proto:47-49: chamfered top-right corner (9px notch). */
    background: var(--cs-ink);
    color: var(--cs-card);
    border-color: var(--cs-ink);
    clip-path: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 0 100%);
}
/* 35.4 audit: proto:49 primary hover is a near-ink dark grey (#37363f), NOT
   green. Reverts a 35.0-era impl override that flipped to green-dark. */
.b2-btn--primary:hover { background: #37363f; border-color: #37363f; color: var(--cs-card); }

.b2-btn--accent {
    background: var(--cs-green);
    color: var(--cs-card);
    border-color: var(--cs-green);
}
.b2-btn--accent:hover { background: var(--cs-green-dark); border-color: var(--cs-green-dark); }

.b2-btn--ghost {
    background: transparent;
    border-color: transparent;
    color: var(--cs-ink);
    padding-inline: var(--cs-space-3);
}
.b2-btn--ghost:hover { background: var(--cs-paper); border-color: var(--cs-hair); }

.b2-btn--quiet { /* nav-bar text-only button (Logout) — matches .b2-nav__link */
    background: transparent;
    border-color: transparent;
    color: var(--cs-ink-soft);
    font-size: 11px;
    padding: var(--cs-space-1) var(--cs-space-2);
}
.b2-btn--quiet:hover { color: var(--cs-ink); background: transparent; }

.b2-btn--destructive {
    background: var(--cs-card);
    color: var(--cs-red);
    border-color: var(--cs-red);
}
.b2-btn--destructive:hover { background: var(--cs-red); color: var(--cs-card); }

.b2-btn[disabled],
.b2-btn[aria-disabled="true"] {
    cursor: not-allowed;
    color: var(--cs-ink-muted);
    background: var(--cs-paper);
    border-color: var(--cs-hair);
}

.b2-btn--small { padding: var(--cs-space-1) var(--cs-space-3); font-size: var(--cs-fs-small); }
/* 35.4 audit: proto:50 `.btn-wide` is the block variant — wider 11px padding +
   centred text so it matches the prototype's auth-form submit buttons. */
.b2-btn--block { width: 100%; padding: 11px; text-align: center; }

/* ----------------------------------------------------------------------------
   Inputs
---------------------------------------------------------------------------- */
.b2-input,
.b2-select,
.b2-textarea {
    display: block;
    width: 100%;
    padding: var(--cs-space-2) var(--cs-space-3);
    font-family: var(--cs-font-sans);
    font-size: var(--cs-fs-body);
    background: var(--cs-card);
    color: var(--cs-ink);
    border: var(--cs-hair-width) solid var(--cs-hair);
    border-radius: 0;
}
.b2-input:focus,
.b2-select:focus,
.b2-textarea:focus {
    outline: 2px solid var(--cs-hair-strong);
    outline-offset: 0;
    border-color: var(--cs-hair-strong);
}
.b2-textarea { font-family: var(--cs-font-mono); font-size: var(--cs-fs-small); min-height: 6rem; }

.b2-label {
    /* 35.4 audit: proto:130-131 `.field label` — mono 11px uppercase
       letter-spacing .03em, ink-soft color, margin-bottom 5px. Replaces the
       impl's plainer 14px sans label. */
    display: block;
    margin-bottom: 5px;
    font-family: var(--cs-font-mono);
    font-size: 11px;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
    font-weight: 500;
}
.b2-help {
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
    color: var(--cs-ink-soft);
    margin-top: var(--cs-space-1);
}
.b2-field { margin-bottom: 14px; /* proto:129 — was var(--cs-space-4) = 16px */ }

.b2-fieldset {
    /* 35.4 audit: proto:144 `.fieldset` — lighter line color (--cs-line vs
       --cs-hair), padding 14px 14px 4px (tighter bottom for floating label),
       margin-bottom 14px (was 16px). */
    border: var(--cs-hair-width) solid var(--cs-line);
    padding: 14px 14px 4px;
    margin-bottom: 14px;
}
/* R1-6 (phase 35.6): the .b2-fieldset__legend block previously sat here
   but was dead — the template used <div class="fs-h"> not <legend>. After
   35.6 the template uses <legend class="fs-h">, so the more specific
   .fs-h rule (b2.css around line 1227) wins, and this duplicate block
   adds no value. Deleted. */

.b2-checkbox {
    display: flex;
    align-items: center;
    gap: var(--cs-space-2);
    font-size: var(--cs-fs-body);
    color: var(--cs-ink);
    cursor: pointer;
}
.b2-checkbox input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--cs-green-dark);
}

/* ----------------------------------------------------------------------------
   Toggle (squared switch)
---------------------------------------------------------------------------- */
.b2-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--cs-space-3);
    padding: var(--cs-space-1) var(--cs-space-2);
    background: transparent;
    border: 0;
    cursor: pointer;
    font-family: var(--cs-font-sans);
    font-size: var(--cs-fs-body);
    color: var(--cs-ink);
    text-align: left;
}
.b2-toggle:focus-visible { outline: 2px solid var(--cs-hair-strong); outline-offset: 2px; }
.b2-toggle__track {
    position: relative;
    width: 38px;
    height: 20px;
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    flex-shrink: 0;
    transition: background 100ms ease, border-color 100ms ease;
}
.b2-toggle__thumb {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 16px;
    height: 16px;
    background: var(--cs-ink-soft);
    transition: transform 120ms ease, background 120ms ease;
}
.b2-toggle[aria-checked="true"] .b2-toggle__track {
    background: var(--cs-green);
    border-color: var(--cs-green);
}
.b2-toggle[aria-checked="true"] .b2-toggle__thumb {
    transform: translateX(18px);
    background: var(--cs-card);
}
.b2-toggle__label { font-weight: 500; }
.b2-toggle__state {
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-eyebrow);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--cs-ink-soft);
}
.b2-toggle[aria-checked="true"] .b2-toggle__state { color: var(--cs-green-dark); }

/* ----------------------------------------------------------------------------
   Tables
---------------------------------------------------------------------------- */
.b2-table-wrap {
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    min-width: 0;
    overflow-x: auto;
}
.b2-table { width: 100%; border-collapse: collapse; font-size: var(--cs-fs-body); }
.b2-table thead { background: var(--cs-paper); }
.b2-table th, .b2-table td {
    padding: var(--cs-space-2) var(--cs-space-4);
    text-align: left;
    border-bottom: var(--cs-hair-width) solid var(--cs-hair);
    vertical-align: top;
}
.b2-table th {
    font-family: var(--cs-font-mono);
    font-weight: 500;
    font-size: var(--cs-fs-small);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--cs-ink-soft);
    white-space: nowrap;
}
.b2-table tbody tr:hover { background: var(--cs-paper); }
.b2-table td.b2-num { font-family: var(--cs-font-mono); text-align: right; }
.b2-table tr.b2-table__sub td { background: rgba(188, 186, 176, 0.15); padding: 0; }

/* ----------------------------------------------------------------------------
   Stat grid (summary cards)
---------------------------------------------------------------------------- */
.b2-stat-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--cs-space-3);
    margin-bottom: var(--cs-space-4);
}
@media (min-width: 720px) {
    .b2-stat-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.b2-stat {
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: var(--cs-space-4);
    position: relative;
}
.b2-stat__label { font-family: var(--cs-font-mono); font-size: var(--cs-fs-eyebrow); text-transform: uppercase; letter-spacing: 0.08em; color: var(--cs-ink-soft); margin-bottom: var(--cs-space-1); }
.b2-stat__value { font-size: var(--cs-fs-subhead); font-weight: 600; color: var(--cs-ink); }

/* Blueprint corner ticks (decorative) */
.b2-stat::before, .b2-stat::after {
    content: "";
    position: absolute;
    width: 6px; height: 6px;
    border: 1px solid var(--cs-hair-strong);
}
.b2-stat::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.b2-stat::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

/* ----------------------------------------------------------------------------
   Banner
---------------------------------------------------------------------------- */
.b2-banner {
    padding: var(--cs-space-3) var(--cs-space-4);
    border: var(--cs-hair-width) solid var(--cs-hair);
    background: var(--cs-card);
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--cs-space-4);
    flex-wrap: wrap;
    margin-bottom: var(--cs-space-4);
}
.b2-banner__body { display: flex; gap: var(--cs-space-3); align-items: flex-start; }
.b2-banner__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px; height: 24px;
    font-family: var(--cs-font-mono);
    font-weight: 700;
}
.b2-banner__title { font-weight: 600; color: var(--cs-ink); margin-bottom: var(--cs-space-1); }
.b2-banner__detail { font-size: var(--cs-fs-body); color: var(--cs-ink-soft); }
.b2-banner--warn { border-color: var(--cs-amber); background: var(--cs-amber-soft); }
.b2-banner--warn .b2-banner__mark { color: var(--cs-amber-dark); }
.b2-banner--warn .b2-banner__detail { color: var(--cs-amber-dark); }
.b2-banner--error { border-color: var(--cs-red); background: var(--cs-red-soft); }
.b2-banner--error .b2-banner__mark { color: var(--cs-red); }
.b2-banner--error .b2-banner__title { color: var(--cs-red); }
.b2-banner--error .b2-banner__detail { color: var(--cs-red); }

/* ----------------------------------------------------------------------------
   Hosts strip
---------------------------------------------------------------------------- */
/* R3-3 (phase 35.6 ride-along): the .b2-host-strip rule that previously
   sat here was dead. Templates use .hoststrip (defined further down the
   file). REWORK-TZ §R3-3 explicitly: "not excused by spec like .b2-host-pill".
   The .b2-host-pill family stays — spec §5.5 reserves it for the future
   multi-host enabled-host pill (see also R3-2 in REWORK-TZ). */
.b2-host-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--cs-space-2);
    padding: var(--cs-space-1) var(--cs-space-3);
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
    color: var(--cs-ink);
}
.b2-host-pill__dot {
    width: 8px; height: 8px;
    background: var(--cs-green);
    display: inline-block;
}
.b2-host-pill--gitlab .b2-host-pill__dot { background: var(--cs-host-gitlab); }
.b2-host-pill--connected .b2-host-pill__dot { background: var(--cs-green); }
.b2-host-pill--coming-soon {
    pointer-events: none;
    color: var(--cs-ink-soft);
    background: var(--cs-paper);
}
.b2-host-pill--coming-soon .b2-host-pill__dot { background: var(--cs-hair); }
.b2-host-pill .b2-eyebrow { margin-left: var(--cs-space-2); color: var(--cs-ink-soft); }

/* ----------------------------------------------------------------------------
   Coming-soon button (login / register / connect-gitlab)
---------------------------------------------------------------------------- */
.b2-coming-soon {
    /* 35.4 audit: aligned to prototype `.hostbtn.disabled` (proto:209-213 +
       proto:211 opacity .5). Solid border + white background + opacity-fade
       matches the prototype's inert host pill look; previously the impl used
       a paper-fill dashed-border pattern that diverged visually from proto.
       Padding 10px (proto:213) replaces var(--cs-space-3) var(--cs-space-4). */
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: 9px;
    width: 100%;
    padding: 10px;
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    color: var(--cs-ink);
    font-family: var(--cs-font-mono);
    font-size: 12px;
    letter-spacing: 0.02em;
    opacity: 0.5;
    pointer-events: none;
    cursor: not-allowed;
    margin-bottom: 8px;
}
/* R1-1 (phase 35.6): templates use <svg class="hi"> inside .b2-coming-soon
   buttons, not <span class="b2-coming-soon__mark">. The previous
   .b2-coming-soon__mark family was dead — the rules below replace it by
   keying the per-host fill on .hi, which is what templates actually emit.
   Spec §5.16: --github / --bitbucket / --gitlab modifiers give brand color
   to the glyph. */
.b2-coming-soon--github    .hi { fill: var(--cs-host-github); }
.b2-coming-soon--bitbucket .hi { fill: var(--cs-host-bitbucket); }
.b2-coming-soon--gitlab    .hi { fill: var(--cs-host-gitlab); }
.b2-coming-soon__label { flex: 1; }

/* ----------------------------------------------------------------------------
   Chip input
---------------------------------------------------------------------------- */
.b2-chip-input {
    border: var(--cs-hair-width) solid var(--cs-hair);
    background: var(--cs-card);
    padding: var(--cs-space-2);
    display: flex;
    flex-wrap: wrap;
    gap: var(--cs-space-2);
    align-items: center;
    min-height: 2.5rem;
}
.b2-chip-input:focus-within { border-color: var(--cs-hair-strong); outline: 2px solid var(--cs-hair-strong); outline-offset: 0; }
.b2-chip-list {
    list-style: none;
    margin: 0; padding: 0;
    display: contents;
}
.b2-chip {
    /* 35.4 audit: proto:148-149 `.chip` — gap 6px (was 4px), padding asymmetric
       3px 4px 3px 8px (was 2px 8px symmetric), background paler #faf9f5 (was
       --cs-paper #ebe9e2 which competes too strongly with the fieldset bg). */
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: #faf9f5;
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: 3px 4px 3px 8px;
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
    color: var(--cs-ink);
}
.b2-chip__remove {
    background: transparent;
    border: 0;
    padding: 0 2px;
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-body);
    line-height: 1;
    color: var(--cs-ink-soft);
    cursor: pointer;
}
.b2-chip__remove:hover { color: var(--cs-red); }
.b2-chip-input__add {
    /* 35.4 audit: proto:152-153 `.chip-add` — dashed 1px border, padding 3px 9px,
       mono 11px, ink-soft color. Functionally it remains an <input> (kept the
       impl's `flex 1 1 8rem` so it fills the wrap), but visually matches the
       prototype's dashed pill affordance. */
    flex: 1 1 8rem;
    min-width: 6rem;
    border: 1px dashed var(--cs-hair);
    outline: 0;
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
    background: transparent;
    color: var(--cs-ink-soft);
    padding: 3px 9px;
    cursor: text;
}
.b2-chip-input__add:focus { border-color: var(--cs-hair-strong); }

/* ----------------------------------------------------------------------------
   Modal
---------------------------------------------------------------------------- */
.b2-modal {
    border: var(--cs-hair-width) solid var(--cs-hair);
    background: var(--cs-card);
    padding: 0;
    color: var(--cs-ink);
    width: min(40rem, 92vw);
    max-height: 80vh;
}
.b2-modal[open] { display: flex; flex-direction: column; }
.b2-modal::backdrop { background: rgba(35, 34, 40, 0.45); }
.b2-modal__header {
    padding: var(--cs-space-4);
    border-bottom: var(--cs-hair-width) solid var(--cs-hair);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cs-space-3);
}
/* 35.4 audit: proto:173 `.m-title` — sans 600 18px (was --cs-fs-subhead = 20px). */
.b2-modal__title { font-family: var(--cs-font-sans); font-size: 18px; font-weight: 600; }
.b2-modal__close {
    background: transparent; border: 0;
    font-family: var(--cs-font-mono); font-size: var(--cs-fs-subhead);
    color: var(--cs-ink-soft); cursor: pointer;
    line-height: 1;
}
.b2-modal__body { padding: var(--cs-space-4); overflow-y: auto; }
.b2-modal__footer {
    padding: var(--cs-space-3) var(--cs-space-4);
    border-top: var(--cs-hair-width) solid var(--cs-hair);
    display: flex;
    justify-content: flex-end;
    gap: var(--cs-space-2);
}

/* Phase 35.7 R2-1 / spec §13.3: .b2-modal--confirm — promise-based
   confirm + alert dialog, sibling variant on .b2-modal. Narrower width
   than the picker modal (28rem vs 40rem). Reuses .b2-modal__header /
   __body / __footer + existing .b2-btn / .b2-btn--ghost / .b2-btn--primary
   / .b2-btn--destructive — no new button styles. Token-only per spec
   §3.5. Single shared <dialog> instance, lazily injected on first call.
   Esc + backdrop click → cancel (native <dialog> behaviour + custom
   backdrop click handler in b2.js). */
.b2-modal--confirm {
    width: min(28rem, 92vw);
}
.b2-modal--confirm .b2-modal__body {
    /* Body copy is short prose (single paragraph in non-destructive,
       multi-sentence in destructive remove) — match the picker modal
       body padding so the visual rhythm carries over. */
    padding: var(--cs-space-4);
    font-family: var(--cs-font-sans);
    font-size: var(--cs-fs-body);
    color: var(--cs-ink);
}

/* Modal-internal project picker */
.b2-picker-list {
    list-style: none;
    padding: 0;
    margin: var(--cs-space-3) 0 0;
    border: var(--cs-hair-width) solid var(--cs-hair);
    max-height: 16rem;
    overflow-y: auto;
}
.b2-picker-item {
    /* 35.4 audit: proto:175-176 `.pk-row` — padding 11px 2px (vertical-heavy),
       lighter --cs-line separator. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cs-space-3);
    padding: 11px 2px;
    border-bottom: var(--cs-hair-width) solid var(--cs-line);
    cursor: pointer;
}
.b2-picker-item:last-child { border-bottom: 0; }
.b2-picker-item:hover { background: var(--cs-paper); }
/* 35.4 audit: proto:177 `.pk-nm` — font-weight 600 + font-size 14px. */
.b2-picker-item__name { font-weight: 600; font-size: 14px; color: var(--cs-ink); }
.b2-picker-item__path { font-family: var(--cs-font-mono); font-size: var(--cs-fs-small); color: var(--cs-ink-soft); }
/* Phase 35.6.1 BLOCKER 2 + cross-AI round 3 (Gemini MAJOR-3 + Codex Probe 5):
   descendant selector lets a SINGLE aria-busy on the .b2-modal dialog cascade
   to every .b2-picker-item without a JS forEach loop. AT announces one busy
   region instead of N busy items (MDN guidance), and the visual paint is
   identical to the previous per-item rule (progress cursor, dimmed, no
   pointer events). No :has() or other modern selector needed. */
.b2-modal[aria-busy="true"] .b2-picker-item {
    opacity: 0.6;
    cursor: progress;
    pointer-events: none;
}
.b2-picker-empty {
    padding: var(--cs-space-3);
    color: var(--cs-ink-soft);
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-small);
}

/* ----------------------------------------------------------------------------
   Save bar
---------------------------------------------------------------------------- */
.b2-savebar {
    /* 35.4 audit: proto:155 `.savebar` — paler background #faf9f5 (was --cs-card
       white), padding 11px 14px (was 12px 16px), full border (proto has all
       four sides; impl had only border-top). Sticky positioning + z-index kept
       intentionally (impl improvement: savebar tracks scroll). */
    position: sticky;
    bottom: 0;
    background: #faf9f5;
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: 11px 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cs-space-3);
    margin-top: 4px;
    z-index: 10;
}
.b2-savebar__status {
    /* 35.4 audit: proto:156 `.dirty` — mono 11px sentence-case (no uppercase,
       no letter-spacing), ink-soft. Was eyebrow-style uppercase. */
    font-family: var(--cs-font-mono);
    font-size: 11px;
    color: var(--cs-ink-soft);
}
/* 35.4 audit: proto:156 `.dirty` adds a small dot + flex layout. The dot
   is implemented via the impl's data-dirty hook on .b2-savebar__status. */
.b2-savebar[data-dirty="true"] .b2-savebar__status {
    color: var(--cs-amber-dark);
    display: inline-flex;
    align-items: center;
    gap: 7px;
}
.b2-savebar[data-dirty="true"] .b2-savebar__status::before {
    content: "";
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--cs-amber-dark);
}
.b2-savebar[data-dirty="true"] {
    border-color: var(--cs-amber);
}
/* Phase 35.6.1 BLOCKER 3: error-state vocabulary for savebar. Quieter
   palette per Q4 — border + dot + text red, paper bg unchanged. Rules
   placed AFTER data-dirty rules so red wins on tied specificity when
   both attributes coexist after a failed save (MAJOR-2 invariant). */
.b2-savebar[data-state="error"] .b2-savebar__status {
    color: var(--cs-red);
    display: inline-flex;
    align-items: center;
    gap: 7px;
}
.b2-savebar[data-state="error"] .b2-savebar__status::before {
    content: "";
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--cs-red);
}
.b2-savebar[data-state="error"] {
    border-color: var(--cs-red);
}
.b2-savebar__actions { display: flex; gap: var(--cs-space-2); }

/* ----------------------------------------------------------------------------
   Quota meter (Phase 48 BILUI-03 — the ONE budgeted net-new CSS family)
   48-UI-SPEC "New primitive budget": token-only, ~8px, square corners, with a
   mono text equivalent beside it (the bar is decorative reinforcement). Fill is
   green by default; amber at >=75% (--warn); red ONLY when consumption exceeds
   the flex cap (--over, a genuine over-limit). No hex literals — tokens only.
---------------------------------------------------------------------------- */
.b2-meter {
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    height: 8px;
    width: 100%;
    overflow: hidden;
}
.b2-meter__fill {
    height: 100%;
    background: var(--cs-green);
    transition: width 0.3s ease-out;
}
.b2-meter--warn .b2-meter__fill { background: var(--cs-amber); }
.b2-meter--over .b2-meter__fill { background: var(--cs-red); }
/* Honor the user's OS reduced-motion preference (mirror the b2-skbar/b2-dots
   reduced-motion blocks) — the fill width snaps with no transition. */
@media (prefers-reduced-motion: reduce) {
    .b2-meter__fill { transition: none; }
}

/* ----------------------------------------------------------------------------
   Resolved-config readout
---------------------------------------------------------------------------- */
.b2-config-list { display: flex; flex-direction: column; gap: var(--cs-space-3); }
.b2-config-section { display: flex; flex-direction: column; gap: var(--cs-space-2); }
.b2-config-section__title {
    /* 35.4 audit: proto:159-160 `.rc-gh` — letter-spacing 0.09em (was 0.08em),
       margin-bottom 7px (was 4px), padding-bottom 5px + border-bottom hairline
       (proto separates each group with an inline divider). */
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-eyebrow);
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--cs-ink-soft);
    margin-bottom: 7px;
    padding-bottom: 5px;
    border-bottom: var(--cs-hair-width) solid var(--cs-line);
}
.b2-config-row {
    /* 35.4 audit: proto:161 `.rc-row` — flush 4px vertical padding, 13px font.
       Dotted border-bottom kept as impl-specific row separator (proto has none
       but impl shows it for scannability of long lists). */
    display: flex;
    justify-content: space-between;
    gap: var(--cs-space-3);
    align-items: flex-start;
    padding: 4px 0;
    font-size: 13px;
    border-bottom: 1px dotted var(--cs-hair);
}
.b2-config-row__key { color: var(--cs-ink-soft); /* proto:162 — was --cs-ink (full black) */ }
.b2-config-row__value { font-family: var(--cs-font-mono); font-size: var(--cs-fs-small); color: var(--cs-ink); text-align: right; word-break: break-all; }
.b2-config-row__value--list { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
/* R3-1 (phase 35.6 ride-along): the .b2-config-row__origin rule that
   previously sat here was a duplicate of the live .rc-origin rule lower
   in the file. The template applies both classes; .rc-origin wins on
   cascade with the spec-correct 9px / .04em / bordered look. Deleted. */

/* ----------------------------------------------------------------------------
   Page section header
---------------------------------------------------------------------------- */
.b2-page-header {
    /* 35.4 audit: proto:53 `.titlerow` — align-items flex-end (was center),
       gap 16px (was 12px), margin-bottom 16px (was 24px). */
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--cs-space-4);
    flex-wrap: wrap;
    margin-bottom: var(--cs-space-4);
}
.b2-section { margin-bottom: var(--cs-space-7); }
.b2-section__head { display: flex; flex-direction: column; gap: var(--cs-space-1); margin-bottom: var(--cs-space-3); }

/* ----------------------------------------------------------------------------
   Status pill (used in stats tables)
---------------------------------------------------------------------------- */
.b2-status {
    display: inline-block;
    padding: 2px var(--cs-space-2);
    border: var(--cs-hair-width) solid var(--cs-hair);
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-eyebrow);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--cs-ink);
}
.b2-status--ok { border-color: var(--cs-green); color: var(--cs-green-dark); }
.b2-status--fail { border-color: var(--cs-red); color: var(--cs-red); }
.b2-status--warn { border-color: var(--cs-amber); color: var(--cs-amber-dark); }
.b2-status--muted { border-color: var(--cs-hair); color: var(--cs-ink-soft); }

/* ----------------------------------------------------------------------------
   Misc helpers
---------------------------------------------------------------------------- */
.b2-stack-1 > * + * { margin-top: var(--cs-space-1); }
.b2-stack-2 > * + * { margin-top: var(--cs-space-2); }
.b2-stack-3 > * + * { margin-top: var(--cs-space-3); }
.b2-stack-4 > * + * { margin-top: var(--cs-space-4); }
.b2-stack-5 > * + * { margin-top: var(--cs-space-5); }

.b2-row { display: flex; align-items: center; gap: var(--cs-space-3); flex-wrap: wrap; }
.b2-row--between { justify-content: space-between; }
.b2-row--end { justify-content: flex-end; }

.b2-grid-2 {
    /* 35.4 audit: proto:142 `.grid2` — gap 18px (was --cs-space-5 = 24px). */
    display: grid;
    grid-template-columns: 1fr;
    gap: 18px;
}
@media (min-width: 900px) {
    .b2-grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* Phase 40 / Fix A — picker spinner + dialog loading shade. Used by
   bindAddProjectModal() while the /enable POST is in flight so the user
   sees immediate visual feedback instead of a blank ~500ms wait. */
.b2-spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 1.5px solid var(--cs-hair);
    border-top-color: var(--cs-ink);
    border-radius: 50%;
    animation: b2-spin 0.7s linear infinite;
    vertical-align: middle;
}
@keyframes b2-spin {
    to { transform: rotate(360deg); }
}
.b2-hidden { display: none !important; }
.b2-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.b2-error-msg { color: var(--cs-red); font-size: var(--cs-fs-body); margin-bottom: var(--cs-space-3); }
.b2-success-msg { color: var(--cs-green-dark); font-size: var(--cs-fs-body); margin-bottom: var(--cs-space-3); }

/* Subtle dotted-grid page background (blueprint motif) */
.b2-page::before {
    content: "";
    position: fixed;
    inset: 0;
    background-image: radial-gradient(circle at 0 0, var(--cs-hair) 1px, transparent 1.2px);
    background-size: 32px 32px;
    opacity: 0.18;
    pointer-events: none;
    z-index: 0;
}
.b2-page > * { position: relative; z-index: 1; }

/* ----------------------------------------------------------------------------
   Phase 35.4 — prototype-aligned primitives (auth pages, host pills, summary)
---------------------------------------------------------------------------- */
/* Auth-screen wrapper, brand block, wordmark — proto:127-129 */
/* Auth pages don't want b2-shell padding+max-width on top of auth-screen's own
   self-centering layout — the sum overflows 100vh and triggers scroll. */
.b2-shell:has(> .auth-screen) {
    max-width: none;
    padding: 0;
}
.auth-screen {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40px 20px; /* proto:127 — exact prototype padding */
}
.auth-brand {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 22px;
}
.mark {
    width: 13px;
    height: 13px;
    background: var(--cs-green);
    transform: rotate(45deg);
    display: inline-block;
    flex: none;
}
.wordmark {
    font-family: var(--cs-font-sans);
    font-weight: 700;
    font-size: 16px;
    letter-spacing: -0.01em;
}
/* Eyebrow inside auth card — proto:172-174 */
.m-eye {
    font-family: var(--cs-font-mono);
    font-size: 10px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
    margin-bottom: 7px;
}
/* SVG glyph sizer used inside hostbtn / hostcard / hostitem — proto:208 */
.hi {
    width: 15px;
    height: 15px;
    fill: currentColor;
    flex: none;
}
/* Host button — proto:209-213 (full-width host pill with leading SVG) */
.hostbtn {
    /* 35.4 audit: proto:209-213 `.hostbtn` — color is full --cs-ink (was
       --cs-ink-soft); proto host buttons read at body weight, not as muted
       secondary controls. */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    width: 100%;
    font-family: var(--cs-font-mono);
    font-size: 12px;
    letter-spacing: 0.02em;
    cursor: pointer;
    border: var(--cs-hair-width) solid var(--cs-hair);
    background: var(--cs-card);
    color: var(--cs-ink);
    padding: 10px;
    margin-bottom: 8px;
}
.hostbtn[aria-disabled="true"],
.hostbtn[disabled],
.hostbtn.disabled {
    opacity: 0.5; /* proto:211 — was 0.6 */
    cursor: not-allowed;
    pointer-events: none;
}
.hostbtn .soon {
    margin-left: auto;
}
/* "or with email" divider — proto:215-217 */
.authdiv {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 15px 0;
    font-family: var(--cs-font-mono);
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
}
.authdiv::before,
.authdiv::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--cs-hair);
}
/* Footer link row — proto:135-136 */
.auth-foot {
    font-family: var(--cs-font-mono);
    font-size: 12px;
    color: var(--cs-ink-soft);
    text-align: center;
    margin-top: 16px;
}
.auth-foot a {
    color: var(--cs-green-dark);
}
/* "Soon" badge — proto:225-227 */
.soon {
    font-family: var(--cs-font-mono);
    font-size: 9px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    border: var(--cs-hair-width) solid var(--cs-hair);
    color: var(--cs-ink-soft);
    padding: 1px 5px;
}
/* Host card — proto:218-222 (/connect-gitlab three-uniform-card layout) */
.hostcard {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: 13px 15px;
    margin-bottom: 9px;
    background: var(--cs-card);
}
.hostcard .hn {
    font-weight: 600;
    font-size: 14px;
}
.hostcard .hs {
    font-family: var(--cs-font-mono);
    font-size: 11px;
    color: var(--cs-ink-soft);
    margin-top: 2px;
}
.hostcard[aria-disabled="true"] {
    opacity: 0.62;
    cursor: default;
}
.hostcard.on {
    box-shadow: inset 3px 0 0 var(--cs-green);
}
/* Host strip on /projects — proto:202-207 */
.hoststrip {
    display: flex;
    align-items: stretch;
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    margin-bottom: 16px;
    flex-wrap: wrap;
}
.hslabel {
    /* 35.4 audit: proto:202-203 `.hslabel` — background #f6f5f0 (paler than
       --cs-paper #ebe9e2); border-right uses --cs-line (lighter) per proto. */
    font-family: var(--cs-font-mono);
    font-size: 10px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
    padding: 0 16px;
    display: flex;
    align-items: center;
    border-right: var(--cs-hair-width) solid var(--cs-line);
    background: #f6f5f0;
}
.hostitem {
    /* 35.4 audit: proto:204 — border-right uses --cs-line (lighter) per proto. */
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 16px;
    border-right: var(--cs-hair-width) solid var(--cs-line);
    font-size: 13px;
}
.hostitem .hn {
    font-weight: 600;
}
.hostitem .hst {
    font-family: var(--cs-font-mono);
    font-size: 11px;
    color: var(--cs-ink-soft);
}
.hostitem.soon-host {
    opacity: 0.65;
}
/* R1-3 (phase 35.6): per-host glyph fills keyed off [data-host]. Replaces
   the inline `style="...fill:#xxxxxx"` attributes the templates used to
   carry. Uses the design tokens so future token changes propagate; also
   corrects the GitHub hue (was `#24292f` GH "Coal", now --cs-host-github
   `#181717` GH "Tar" per spec §2.1). */
.hostitem[data-host="github"]    .hi { fill: var(--cs-host-github); }
.hostitem[data-host="gitlab"]    .hi { fill: var(--cs-host-gitlab); }
.hostitem[data-host="bitbucket"] .hi { fill: var(--cs-host-bitbucket); }
.sdot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: none;
    background: var(--cs-green);
}
/* Summary strip (blueprint motif) — proto:71-83 */
.summary {
    position: relative;
    display: flex;
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    margin-bottom: 18px;
}
.metric {
    /* 35.4 audit: proto:65 `.metric` — border-right uses --cs-line (lighter)
       per proto, matches the rest of the host-strip / fieldset family. */
    flex: 1;
    padding: 13px 18px;
    border-right: var(--cs-hair-width) solid var(--cs-line);
}
.metric:last-child {
    border-right: 0;
}
.metric .num {
    font-family: var(--cs-font-sans);
    font-weight: 700;
    font-size: 21px;
    line-height: 1;
}
.metric .lbl {
    font-family: var(--cs-font-mono);
    font-size: 10px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
    margin-top: 5px;
}
.tick {
    position: absolute;
    width: 7px;
    height: 7px;
}
.tick.tl { top: -1px; left: -1px; border-top: 1px solid var(--cs-ink); border-left: 1px solid var(--cs-ink); }
.tick.tr { top: -1px; right: -1px; border-top: 1px solid var(--cs-ink); border-right: 1px solid var(--cs-ink); }
.tick.bl { bottom: -1px; left: -1px; border-bottom: 1px solid var(--cs-ink); border-left: 1px solid var(--cs-ink); }
.tick.br { bottom: -1px; right: -1px; border-bottom: 1px solid var(--cs-ink); border-right: 1px solid var(--cs-ink); }

/* 35.4 R9 / D-17: floating fieldset heading on /projects/<path>/settings. */
.b2-fieldset { position: relative; }
.fs-h {
    font-family: var(--cs-font-mono);
    font-size: 10px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--cs-ink-soft);
    position: absolute;
    top: -7px;
    left: 10px;
    background: var(--cs-card);
    padding: 0 6px;
}
/* 35.4 R9 / D-16: resolved-config prototype-aliased classes (additive; existing b2-config-* rules unchanged). */
.rc-group { margin-bottom: 14px; }
.rc-row {
    display: flex;
    justify-content: space-between;
    gap: 14px;
    padding: 4px 0;
    font-size: 13px;
}
.rc-origin {
    /* 35.4 audit: proto:164-165 `.rc-origin` — a tiny badge with hairline
       border, padding 0 4px, margin-left 7px, ink-muted text (not ink-soft).
       Was unstyled inline text. */
    font-family: var(--cs-font-mono);
    font-size: 9px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--cs-ink-muted);
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: 0 4px;
    margin-left: 7px;
}

/* ----------------------------------------------------------------------------
   Phase 35.7 R2-2 / spec §13.4 + §5.19 — .b2-toast snackbar.
   Fixed bottom-right stack, FIFO queue (cap 5 in JS), 4s auto-dismiss
   default, hover-to-pause, click-dismiss. Token-only — no hex literals.
   --------------------------------------------------------------------------- */
.b2-toast-stack {
    position: fixed;
    bottom: var(--cs-space-4);
    right: var(--cs-space-4);
    /* Above .b2-savebar (z-index 10) so a savebar paint cannot obscure
       a result toast firing from a partial-swap success branch. */
    z-index: 100;
    display: flex;
    flex-direction: column;
    gap: var(--cs-space-2);
    /* Q8 — aria-live="polite" lives on the stack root via the JS
       helper; tests pin attribute presence. Width capped per spec. */
    max-width: min(28rem, calc(100vw - 2 * var(--cs-space-4)));
    pointer-events: none;
}
.b2-toast {
    /* Spec §13.4: width clamp, card bg, hairline, padding 11px 14px
       like the savebar, body font/color from existing tokens. */
    width: min(28rem, calc(100vw - 2 * var(--cs-space-4)));
    background: var(--cs-card);
    border: var(--cs-hair-width) solid var(--cs-hair);
    padding: 11px 14px;
    font-family: var(--cs-font-sans);
    font-size: var(--cs-fs-body);
    color: var(--cs-ink);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cs-space-3);
    /* Subtle slide-in animation from the right; killed under
       prefers-reduced-motion below. */
    transform: translateX(0);
    transition: transform 200ms ease, opacity 200ms ease;
    /* Re-enable interaction on the toast itself (stack disables
       pointer-events so the screen behind isn't blocked, then each
       toast opts back in). */
    pointer-events: auto;
}
.b2-toast--enter {
    transform: translateX(110%);
    opacity: 0;
}
.b2-toast--leaving {
    transform: translateX(110%);
    opacity: 0;
}
.b2-toast__body {
    flex: 1;
    /* Word-wrap long error strings (e.g. "Network error while removing
       fintech/payments-with-a-long-namespace.") */
    overflow-wrap: anywhere;
}
.b2-toast__close {
    background: transparent;
    border: 0;
    font-family: var(--cs-font-mono);
    font-size: var(--cs-fs-subhead);
    color: var(--cs-ink-soft);
    cursor: pointer;
    line-height: 1;
    padding: 0 var(--cs-space-2);
}
.b2-toast__close:hover { color: var(--cs-ink); }

/* Variants: left-accent inset shadow (cheap, doesn't disturb hairline).
   Success uses --cs-green-dark; error uses --cs-red; info has no accent. */
.b2-toast--success {
    box-shadow: inset 3px 0 0 var(--cs-green-dark);
}
.b2-toast--error {
    box-shadow: inset 3px 0 0 var(--cs-red);
}
/* .b2-toast--info: no accent, base style suffices. */

@media (prefers-reduced-motion: reduce) {
    .b2-toast {
        transition: none;
    }
    .b2-toast--enter,
    .b2-toast--leaving {
        transform: translateX(0);
        opacity: 1;
    }
}

/* ----------------------------------------------------------------------------
   Phase 35.7 R2-3 / spec §13.5 — .b2-card--removing animate-out before
   the JS removes the card from DOM after a successful DELETE. pointer-
   events: none keeps the toggle / remove buttons unclickable during the
   220ms animation window so a user click cannot trigger a second action
   on a half-removed card (T-35.7-08 race mitigation).
   --------------------------------------------------------------------------- */
.b2-card--removing {
    animation: b2-card-collapse 220ms ease forwards;
    pointer-events: none;
}
@keyframes b2-card-collapse {
    0%   { opacity: 1; max-height: 600px; }
    100% { opacity: 0; max-height: 0; padding-block: 0; margin-block: 0; }
}
@media (prefers-reduced-motion: reduce) {
    /* Reduced-motion path: no animation, no animationend event. The JS
       matchMedia() check short-circuits the await on animationend in
       that branch — see b2SwapProjectCard / remove-project handler. */
    .b2-card--removing {
        animation: none;
        opacity: 0;
    }
}

/* ---------------------------------------------------------------------------
   Phase 44 — Projects loading UX (TASK-44-012)
   Satisfies MH-44-17, MH-44-29 and AC-44-025/035.
   All loaders animate `opacity` only — no transform / layout properties so
   the GPU can keep them off the main thread (DESIGN-SPEC §6.4).
   --------------------------------------------------------------------------- */

/* Three pin-square loader (B2 brand pin-mark motif, DESIGN-SPEC §6.4). */
.b2-dots {
    display: inline-flex;
    gap: 3px;
    align-items: center;
}
.b2-dots span {
    width: 8px;
    height: 8px;
    background: var(--cs-green);
    display: inline-block;
    animation: b2-pulse 1.2s ease-in-out infinite;
}
.b2-dots span:nth-child(2) { animation-delay: 0.2s; }
.b2-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes b2-pulse {
    0%, 80%, 100% { opacity: 0.25; }
    40% { opacity: 1; }
}

/* Status pill (mono, 11px uppercase, amber warn variant). */
.b2-pill {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 5px 11px;
    background: var(--cs-card);
    border: 1px solid var(--cs-hair);
    font-family: var(--cs-font-mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--cs-ink-soft);
    /* MH-44-29 mobile cap: avoid uppercase + letter-spacing overflow. */
    max-width: clamp(8rem, 60vw, 22rem);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.b2-pill--warn {
    border-color: var(--cs-amber);
    color: var(--cs-amber-dark);
    background: var(--cs-amber-soft);
}
.b2-pill__counter {
    font-variant-numeric: tabular-nums;
}

/* Skeleton bar — opacity-only pulse. */
.b2-skbar {
    background: var(--cs-line);
    height: 10px;
    animation: b2-skp 1.4s ease-in-out infinite;
}
.b2-skbar--thin { height: 8px; }
.b2-skbar--mono { background: #e3e1d8; }
@keyframes b2-skp {
    0%, 100% { opacity: 0.55; }
    50% { opacity: 0.95; }
}

/* Stale-card opacity transition during SWR refresh. */
.b2-card--stale {
    opacity: 0.85;
    transition: opacity 0.25s ease-out;
}

/* Project-card-shaped skeleton placeholder (composes b2-skbar). */
.b2-pcard--skeleton {
    background: var(--cs-card);
    border: 1px solid var(--cs-hair);
    padding: 14px 16px;
    margin-bottom: 10px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Auth-revoked variant — cached cards visible but dimmed (MH-44-26). */
.b2-card--auth-revoked {
    opacity: 0.85;
}

/* MH-44-29 — reduced-motion respects the user's OS preference. */
@media (prefers-reduced-motion: reduce) {
    .b2-dots span {
        animation-name: none;
    }
    .b2-skbar {
        animation-name: none;
    }
}

/* MH-44-29 — narrow-viewport pill cap: hide the live counter so the
   uppercase + letter-spacing label doesn't overflow on phones. */
@media (max-width: 640px) {
    .b2-pill .b2-pill__counter { display: none; }
}
