/* ============================================================
   Bid Scout — mArshline identity
   Shares its tokens with the marketing site (marshlineai.com, site.css):
   sky blue (the line and hook), navy (the wordmark), red (the "AI"),
   Quicksand headings over Inter body, light theme only — the navy
   wordmark does not read on dark grounds. White cards on a blue-tinted
   page, thin light borders, soft navy shadows, rounded corners.

   Legacy token names (--gold, --cream, --serif) are kept as aliases so
   the pages written against the old CSE theme keep working; they point
   at the new accent, ground, and heading face.

   The identity itself is written down once, in the marketing repo
   (fishbait-marketing/BRAND.md); these values are a by-hand mirror of
   its site.css. Change one, change both — see docs/brand.md.
   ============================================================ */

:root {
    color-scheme: light;

    --blue: #0d94fe;
    --blue-dark: #0a76cc;
    --blue-soft: #e6f3ff;
    --blue-tint: #f4f9ff;
    --navy: #053179;
    --navy-deep: #06224f;
    --navy-ink: #0f1f3d;
    --red: #ea0417;

    --ink: #0f1f3d;
    --ink-2: #3b4a66;
    /* US-13.4.2 (STY-05): darkened from #66748f (4.45:1 on --bg, fails AA) to
       the interaction-patterns sheet's own "muted 14 at #55637f" reference —
       5.71:1 on --bg and 6.04:1 on white, comfortably past 4.5:1 wherever
       .muted or table.list th render on either surface. */
    --muted: #55637f;
    --line: #dfe6f1;
    --bg: #f5f9ff;
    --card: #ffffff;
    /* US-13.2.1 (STY-02): a field's own edge, visible before it is
       focused — about 3.4:1 on white, well past the AA 3:1 non-text floor.
       --line stays reserved for decorative card borders and rules, never a
       form control, so the two can move independently. */
    --field-border: #7f8ca3;

    --ok: #14833b;
    --ok-bg: #e7f6ec;
    --warn: #9a5b00;
    --warn-bg: #fff4dc;
    --danger: #c3020f;          /* the brand red, deepened for text on white */
    --danger-bg: #fdeaec;

    --radius: 14px;
    --radius-sm: 8px;
    --shadow: 0 2px 8px rgba(5, 49, 121, 0.08);
    --shadow-lg: 0 12px 32px rgba(5, 49, 121, 0.10);

    --head: "Quicksand", "Segoe UI", system-ui, sans-serif;
    --sans: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;

    /* legacy names */
    --gold: var(--blue-dark);
    --gold-strong: var(--navy);
    --gold-soft: var(--blue-soft);
    --cream: var(--bg);
    --serif: var(--head);
}

* { box-sizing: border-box; }

/* US-13.4.1 (STY-10): 100%, not a fixed 16px — every rem size in this file
   (badges, small buttons, table headers, .muted, all of it) then tracks the
   browser/OS text-size preference (Chrome "Very large", Firefox's Fonts
   size, iOS/Android system text size) instead of overriding it. Page zoom
   already worked either way; this is the separate "make text bigger"
   setting some customers over 55 rely on instead of zooming every page. */
html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    font-size: 100%;
    line-height: 1.55;
}

h1, h2, h3, h4 {
    font-family: var(--head);
    font-weight: 700;
    color: var(--navy-deep);
    line-height: 1.2;
    letter-spacing: -0.01em;
    margin: 0 0 .5rem 0;
}
h1 { font-size: 2.2rem; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.15rem; }
h1:focus { outline: none; }
/* F13.5/US-13.5.2: the same "focused only by script, never by Tab" case as
   h1:focus above, now for the handful of h2 headings a page focuses to catch
   keyboard focus after a row it removed (Team's "Members", Profile's own
   h2) — never a heading a person can actually Tab to on their own. */
h2:focus { outline: none; }

a { color: var(--blue-dark); }
a:hover { color: var(--navy); }

/* ---------- chrome ---------- */

/* US-02.3.2: the first thing on every page, hidden off-screen until it is
   itself the focused element (never display:none/visibility:hidden, which
   would also hide it from a screen reader). */
.skip-link {
    position: absolute;
    top: -3rem;
    left: 0;
    z-index: 1200;
    background: var(--navy-deep);
    color: #fff;
    font-weight: 600;
    padding: .6rem 1.1rem;
    border-radius: 0 0 8px 0;
    text-decoration: none;
}
.skip-link:focus {
    top: 0;
    outline: 3px solid var(--blue);
    outline-offset: -3px;
}

/* One-row header (US-02.1.1/F02.1): brand, the five-word primary nav and the
   account menu all sit on a single line at 1024px and wider — five short
   words never crowd the brand the way the old sixteen-link row did, so there
   is no second, ruled nav row to make space for them. */
.topbar {
    background: var(--card);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow);
    padding: .55rem 1.25rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem 1.25rem;
}
.brand {
    display: flex; align-items: center; gap: .65rem;
    text-decoration: none;
}
.brand .logo { height: 44px; width: auto; display: block; }
.brand .divider { color: var(--line); font-weight: 300; font-size: 1.6rem; line-height: 1; }
.brand .name { font-family: var(--head); font-weight: 700; color: var(--navy-deep); font-size: 1.15rem; }
.brand .tag { font-size: .72rem; color: var(--muted); margin-left: .35rem; }

/* US-02.1.1: the customer nav (<nav aria-label="Main">) — inline with the
   brand and account menu, in source order, no order/flex-basis override and
   no ruled second row. The admin console's grouped nav (F02.6) still gets
   its own wrapped second row below, since fifteen links grouped by job were
   never going to fit beside the brand on one line — see .topbar nav.admin-nav. */
/* US-13.3.3 (STY-03): rows were 1.6px apart at .1rem .15rem — raised so the
   wrapped nav never reads as one crowded block, ahead of the pointer:coarse
   44px-tall links below. */
.topbar nav[aria-label="Main"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem .25rem;
}
.topbar nav.admin-nav {
    order: 3;                 /* below brand + userbox */
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .25rem;
    margin: .15rem -.35rem 0;
    padding: .25rem 0 .45rem;
    border-top: 1px solid var(--line);
}
.topbar nav a {
    text-decoration: none; color: var(--ink-2); font-weight: 500; font-size: .88rem;
    padding: .28rem .55rem; border-radius: 8px;
    white-space: nowrap;
}
.topbar nav a:hover { background: var(--blue-tint); color: var(--navy); }
/* US-02.3.1: current section — bold plus a 2px brand-blue bar, on top of
   (not instead of) the hover tint above. */
.topbar nav a.current {
    font-weight: 700;
    color: var(--navy-deep);
    box-shadow: inset 0 -2px 0 0 var(--blue-dark);
}
.topbar .spacer { flex: 1; }

/* ---------- admin band (F15.1/US-15.1.1) ----------
   The one visual signal that a staff console page is never a customer one:
   AdminLayout adds "admin" to .topbar for a navy (--navy-deep) band instead
   of the customer header's white card, matching admin-layout.svg's #06224f
   header. Same light-on-navy palette the footer band already established
   (#e9f0ff body text at well past 4.5:1, #ff6b73 for the brand's red "AI") —
   see .footerband above. The account menu button, its panel and the phone
   Menu button are unaffected: each is already a self-contained light pill
   that reads fine on either background, so only the elements that sit
   directly on the bar need a color here. The brand mark keeps the
   unmodified logo asset (never re-derive brand art, docs/brand.md) inside a
   small white chip, since the wordmark's own navy text does not read on a
   navy ground (see this file's header comment). */
.topbar.admin { background: var(--navy-deep); border-bottom-color: var(--navy); }
.topbar.admin .brand .logo { background: #fff; border-radius: var(--radius-sm); padding: 3px 6px; }
.topbar.admin .brand .divider { color: rgba(255, 255, 255, .35); }
.topbar.admin .brand .name { color: #fff; }
.topbar.admin nav a { color: #e9f0ff; }
.topbar.admin nav a:hover { background: rgba(255, 255, 255, .14); color: #fff; }
.topbar.admin nav a.current { color: #fff; box-shadow: inset 0 -2px 0 0 #7fc4ff; }
.topbar.admin .admin-nav-group-label { color: rgba(255, 255, 255, .6); }
.topbar.admin .admin-nav-group + .admin-nav-group { border-left-color: rgba(255, 255, 255, .25); }

/* ---------- admin console nav (F02.6/US-02.6.1, F15.1/US-15.1.1) ----------
   Grouped by job (Operate, Customers, Growth) instead of one flat row; group
   labels are plain text, never headings, so the page's own h1/h2 outline
   starts clean inside <main>. Reuses .topbar nav a for link styling
   (hover tint, the current-page bar) so a marked admin link looks exactly
   like a marked customer one. */
.admin-nav-group {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .1rem .6rem;
    padding: .15rem 0;
}
.admin-nav-group + .admin-nav-group {
    margin-left: .5rem;
    padding-left: .75rem;
    border-left: 1px solid var(--line);
}
.admin-nav-group-label {
    font-size: .68rem;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .06em;
    white-space: nowrap;
}
.admin-nav-group-links { display: flex; flex-wrap: wrap; gap: .1rem .15rem; }

/* US-15.2.2: Support's open-ticket count and Queues' duplicates + delivery
   problems, next to the link's own label — hidden entirely at 0 (the markup
   only renders it when the count is positive), refreshed on navigation. */
.admin-nav-badge {
    display: inline-block; margin-left: .3rem; padding: 0 .4rem; min-width: 1.1rem;
    border-radius: 999px; background: var(--danger); color: #fff;
    font-size: .68rem; font-weight: 700; line-height: 1.3rem; text-align: center;
}

/* The drawer toggle only exists on a phone; the desktop bar above is the
   default and needs no button at all. */
.admin-menu-btn {
    display: none;
    align-items: center;
    justify-content: center;
    min-height: 44px; min-width: 44px;
    padding: .4rem .9rem;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
    font: inherit; font-weight: 600; color: var(--navy-deep); cursor: pointer;
}

@media (max-width: 720px) {
    .admin-menu-btn { display: inline-flex; }

    /* .topbar nav {display:flex} outranks a bare .admin-nav selector
       (one class vs. one class + one element) — this has to match or beat
       that specificity to actually hide/reopen the nav on a phone. */
    .topbar nav.admin-nav { display: none; }
    .topbar nav.admin-nav.open {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        position: fixed;
        inset: 3.5rem 0 0 0;
        z-index: 1080;
        background: var(--card);
        border-top: 1px solid var(--line);
        overflow-y: auto;
        padding: .5rem 1rem 1rem;
        margin: 0;
    }
    .admin-nav.open .admin-nav-group {
        flex-direction: column;
        align-items: flex-start;
        gap: .1rem;
        padding: .6rem 0;
    }
    .admin-nav.open .admin-nav-group + .admin-nav-group {
        margin-left: 0;
        padding-left: 0;
        border-left: 0;
        border-top: 1px solid var(--line);
    }
    .admin-nav.open .admin-nav-group-links { flex-direction: column; align-items: stretch; }
    .admin-nav.open .admin-nav-group-links a { min-height: 44px; padding: .5rem .55rem; }
    /* The open drawer is its own white overlay (background: var(--card)
       above), not the navy bar itself — the light-on-navy palette from the
       "admin band" block would be nearly invisible here, so this resets
       back to the original light-theme link colors for exactly this state. */
    .topbar.admin nav.admin-nav.open a { color: var(--ink-2); }
    .topbar.admin nav.admin-nav.open a:hover { background: var(--blue-tint); color: var(--navy); }
    .topbar.admin nav.admin-nav.open a.current { color: var(--navy-deep); box-shadow: inset 0 -2px 0 0 var(--blue-dark); }
    .topbar.admin .admin-nav.open .admin-nav-group-label { color: var(--muted); }
    .admin-drawer-scrim {
        position: fixed; inset: 3.5rem 0 0 0; z-index: 1070;
        background: rgba(6, 34, 79, .35); border: 0; padding: 0; margin: 0; cursor: default;
    }
}

.userbox { display: flex; align-items: center; gap: .75rem; font-size: .9rem; }

/* ---------- account menu (F02.2/US-02.2.1) ----------
   Replaces the old company badge, the loose ".who" name text and the
   standalone Sign out button with one menu behind the company name. The
   scrim is how outside-click-to-close works with no JS: a transparent,
   full-viewport layer under the panel that a click anywhere else lands on. */
.account-menu { position: relative; }
.account-menu-btn {
    display: flex; align-items: center; gap: .4rem;
    min-height: 44px; padding: .4rem .8rem;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
    font: inherit; font-weight: 600; color: var(--navy-deep); cursor: pointer;
}
.account-menu-btn[aria-expanded="true"] { background: var(--blue-tint); border-color: var(--navy); }
/* US-13.8.1 (undefined class): the button's own name/company text — capped
   so a long company name wraps the button, not the header row it sits in. */
.account-menu-label { max-width: 12rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-menu-chevron { color: var(--muted); font-size: .8rem; }
.account-menu-scrim { position: fixed; inset: 0; z-index: 1090; background: transparent; border: 0; padding: 0; margin: 0; cursor: default; }
.account-menu-panel {
    position: absolute; right: 0; top: calc(100% + 6px); z-index: 1100;
    min-width: 240px; max-width: 20rem;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg); padding: .4rem 0;
}
.account-menu-name { padding: .5rem 1rem .6rem; font-weight: 600; color: var(--navy-deep); border-bottom: 1px solid var(--line); margin-bottom: .3rem; }
.account-menu-companies { padding-bottom: .3rem; margin-bottom: .3rem; border-bottom: 1px solid var(--line); }
.account-menu-section-label { padding: .3rem 1rem; font-size: .72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.account-menu-item, .account-menu-static {
    display: flex; align-items: center; gap: .4rem;
    min-height: 44px; width: 100%; box-sizing: border-box;
    padding: .5rem 1rem; font: inherit; font-weight: 500; color: var(--ink);
    text-decoration: none; background: none; border: 0; text-align: left; cursor: pointer;
}
.account-menu-static { color: var(--ink); cursor: default; }
a.account-menu-item:hover, button.account-menu-item:hover,
.account-menu-item:focus-visible {
    background: var(--blue-tint); color: var(--navy); outline: none;
}
.account-menu-item.current { font-weight: 700; color: var(--navy-deep); }
.account-menu-check { color: var(--blue-dark); }
.account-menu-sep { height: 1px; background: var(--line); margin: .35rem 0; }
.account-menu-signout { margin: 0; }

@media (max-width: 720px) {
    .account-menu-panel { position: fixed; left: 0; right: 0; top: 3.5rem; min-width: 0; max-width: none; border-radius: 0; max-height: calc(100vh - 3.5rem); overflow-y: auto; }
}

/* ---------- phone Menu drawer (F02.4/US-02.4.1, reused by F02.7/US-02.7.1) ----------
   Hidden at every width by default — like .admin-menu-btn, this only ever
   shows inside the max-width:720px block below, and only on .has-mobile-menu
   (MainLayout's own comment: a signed-in header with a company selected, or
   a genuine visitor's header — never both). At 721px and wider the inline
   nav/account menu (or the visitor nav/Sign in button) show instead. The
   markup itself differs (MobileMenu.razor vs. VisitorMobileMenu.razor) but
   shares every class here, plus the same MobileMenu.razor.js module. */
.mobile-menu-btn {
    display: none;
    align-items: center;
    gap: .35rem;
    min-height: 44px; min-width: 44px;
    padding: .4rem .9rem;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-sm);
    font: inherit; font-weight: 600; color: var(--navy-deep); cursor: pointer;
}
.mobile-menu-icon { font-size: 1.1rem; line-height: 1; }

.mobile-drawer-scrim {
    position: fixed; inset: 0; z-index: 1190;
    background: rgba(15, 31, 61, .35); border: 0; padding: 0; margin: 0; cursor: default;
}
/* role="dialog", opened from the right — width is a plain percentage with no
   max-width cap so it stays >=80% of the viewport across the whole
   max-width:720px range this rule lives in (a fixed rem cap would fall under
   80% on a 700px-wide small tablet). Always rendered in the DOM (so the
   Menu button's aria-controls="mobile-drawer" resolves to a real element
   even while closed — the same reason nav.admin-nav above is always
   rendered and CSS-class-toggled rather than @if-removed) and shown/hidden
   with the .open class instead of conditional markup. */
.mobile-drawer {
    display: none;
}
.mobile-drawer.open {
    position: fixed; top: 0; right: 0; bottom: 0; z-index: 1200;
    width: 85%;
    background: var(--card);
    box-shadow: var(--shadow-lg);
    display: flex; flex-direction: column;
    overflow-y: auto;
}
.mobile-drawer-head {
    display: flex; justify-content: flex-end;
    padding: .4rem .75rem;
    border-bottom: 1px solid var(--line);
}
.mobile-drawer-close {
    min-height: 44px; padding: .4rem 1rem;
    background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
    font: inherit; font-weight: 600; color: var(--navy-deep); cursor: pointer;
}
.mobile-drawer-section { display: flex; flex-direction: column; }
.mobile-drawer a, .mobile-drawer-static, .mobile-drawer-signout button {
    display: flex; align-items: center;
    min-height: 44px; width: 100%; box-sizing: border-box;
    padding: .5rem 1rem; font: inherit; font-weight: 500; color: var(--ink);
    text-decoration: none; background: none; border: 0; text-align: left; cursor: pointer;
}
.mobile-drawer a:hover { background: var(--blue-tint); color: var(--navy); }
.mobile-drawer a.current {
    font-weight: 700; color: var(--navy-deep);
    box-shadow: inset 3px 0 0 0 var(--blue-dark);
}
.mobile-drawer-section-label { padding: .3rem 1rem; font-size: .72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.mobile-drawer-static { color: var(--ink); }
.mobile-drawer-sep { height: 1px; background: var(--line); margin: .3rem 0; }
.mobile-drawer-signout { margin: 0; }
.mobile-drawer-signout button { width: 100%; }

/* ---------- bottom tab bar (F02.4/US-02.4.2) ----------
   Home, Matches, Pipeline, Ask, My agent under the thumb — same five routes
   as the header nav, current-ness from the same NavigationRules.SectionFor
   helper (US-02.3.1) so a route never disagrees between the two. Hidden
   above 720px like the drawer above; unlike the drawer it is not optional
   once open — it is always on screen at that width, hence no scrim/dialog. */
.bottom-tabbar { display: none; }

@media (max-width: 720px) {
    /* Tighter than the general .topbar padding below: the Menu button's own
       44px hit-target height already fills most of the US-02.4.1 56px header
       budget, so this row gets slightly less top/bottom padding than a
       shorter 36px logo alone would need. */
    .topbar.has-mobile-menu { padding-top: .3rem; padding-bottom: .3rem; }
    .topbar.has-mobile-menu .mobile-menu-btn { display: inline-flex; }
    .topbar.has-mobile-menu nav[aria-label="Main"],
    .topbar.has-mobile-menu .userbox { display: none; }

    .bottom-tabbar {
        display: flex;
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 1150;
        background: var(--card);
        border-top: 1px solid var(--line);
        padding-bottom: env(safe-area-inset-bottom);
    }
    .bottom-tab {
        flex: 1 1 0; min-width: 64px; min-height: 44px;
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        gap: .2rem; padding: .35rem 0;
        text-decoration: none; color: var(--ink-2);
    }
    .bottom-tab-icon {
        width: 18px; height: 18px; border-radius: 50%;
        background: var(--blue-soft);
    }
    .bottom-tab-label { font-size: .75rem; font-weight: 500; }
    .bottom-tab.current .bottom-tab-icon { background: var(--blue-dark); }
    .bottom-tab.current .bottom-tab-label { color: var(--blue-dark); font-weight: 700; }

    /* The page's own bottom padding matches the bar's height plus the safe
       area, so the last row of content and the footer's Support link can
       still scroll fully into view instead of ending up underneath it. */
    main.has-bottom-tabbar { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }

    /* US-02.4.2: the bar would otherwise sit above the on-screen keyboard
       while asking a question, so it hides whenever the Ask composer
       textarea (AgentChat.razor's "chat-composer") has focus. */
    body:has(.chat-composer:focus) .bottom-tabbar { display: none; }
}

.footerband {
    margin-top: 3rem;
    background: var(--navy-deep);
    color: #e9f0ff;
    padding: 1.25rem;
    text-align: center;
    font-size: .9rem;
}
.footerband .gold { color: #7fc4ff; }
.footerband .ai { color: #2fd0f7; }   /* the brand cyan A and i, as in the logo, on the navy band */

/* US-02.7.2 (F02.7): one rule for every footer link (the signed-in link row
   and the visitor footer's "What we cover"/"Help"/Terms/Privacy/mailto set
   alike) instead of the inline style="color:inherit" attributes this used to
   need on each <a> — #e9f0ff on navy-deep (#06224f) is the same pairing the
   surrounding footer text already relies on, well past 4.5:1. */
.footerband a {
    color: inherit;
    text-decoration: underline;
}
.footerband a:hover { color: #fff; }
.footerband a:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (max-width: 720px) {
    /* display:inline-block is what lets min-height actually apply to an
       inline link — a plain inline <a> ignores it — so each word in the
       footer's link row is its own 44px-tall tap target even though the row
       itself still wraps as plain centred text. */
    .footerband a {
        display: inline-block;
        min-height: 44px;
        line-height: 44px;
    }
}

.page { max-width: 1080px; margin: 0 auto; padding: 1.5rem 1.25rem; }
.page.narrow { max-width: 640px; }

/* US-13.4.4 (BILL-18): Terms/Privacy read at a comfortable line length
   inside the full 1080px .page instead of running edge to edge — LegalPage
   already puts this class on its card. */
.legal { max-width: 72ch; line-height: 1.6; }
.legal-toc { margin: 0 0 1.25rem; }
.legal-toc ul { margin: .4rem 0 0 1.2rem; padding: 0; }
.legal-toc li { margin: .25rem 0; }

/* ---------- support-access banner (C02) ---------- */

.support-banner {
    background: var(--blue-soft);
    color: var(--navy-ink);
    font-weight: 700;
    text-align: center;
    padding: .5rem 1rem;
    border-bottom: 2px solid var(--blue);
}

/* Staff impersonation. Visually distinct from the customer banner on purpose:
   a staff member in here is making real changes to a customer's account, and
   the mistake this is guarding against is not noticing which account you are
   in. It must not read as ordinary page furniture. */
.support-banner.staff {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    flex-wrap: wrap;
    background: var(--danger);
    color: #fff;
    border-bottom: 2px solid rgba(0, 0, 0, .35);
}

.support-banner.staff .btn {
    background: #fff;
    color: var(--danger) !important;
    border-color: #fff;
    flex: 0 0 auto;
}

/* Customer-visible support-access banner (F11.5/US-11.5.5, STY-12): read as
   help the Owner agreed to — who, why, since when, and (Owner only) a way to
   end it — never the shouting warning-triangle styling above, which is
   reserved for staff impersonating a customer, not for the customer's own
   view of their own account. */
.support-banner.info {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    font-weight: 400;
    text-align: left;
    background: var(--blue-tint);
    color: var(--navy-ink);
    border-bottom: 2px solid var(--blue);
}
.support-banner.info a { color: inherit; text-decoration: underline; }
.support-banner.info .btn { flex: 0 0 auto; }

/* F15.4/US-15.4.2: one line per open support session in the Accounts list's
   actions cell — wraps normally even though that cell is otherwise nowrap
   (its plain action buttons), since "Opened by … reason: …" can run long. */
.session-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    white-space: normal;
    margin-bottom: .35rem;
}

/* ---------- billing-state banner (F01.5/US-01.5.1) ---------- */

.billing-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    flex-wrap: wrap;
    text-align: center;
    padding: .55rem 1rem;
    font-weight: 600;
    border-bottom: 2px solid transparent;
}
.billing-banner .btn { flex: 0 0 auto; }
.billing-banner .ask { font-weight: 400; }
.billing-banner.warning { background: var(--warn-bg); color: var(--navy-ink); border-bottom-color: var(--warn); }
.billing-banner.danger { background: var(--danger-bg); color: var(--danger); border-bottom-color: var(--danger); }
/* US-13.1.1 (STY-06): the same --ok-on-ok-bg pair as .alert.success below
   (4.3:1) on the one other "success" surface that used it verbatim. */
.billing-banner.success { background: var(--ok-bg); color: #0f6b30; border-bottom-color: var(--ok); }

/* ---------- cards & sections ---------- */

.card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.25rem;
}
.card.plain { box-shadow: none; }
.card.feature { border-left: 4px solid var(--blue); }
.card.danger-zone { border-color: var(--danger); }
.card.danger-zone h2, .card.danger-zone h3 { color: var(--danger); }

/* US-11.1.1 (BILL-17): two numbered steps inside ONE card ("Start your
   subscription") — a divider between them so they read as a sequence, not
   two more equal-weight cards competing for attention. */
.step + .step { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--line); }
.step h3 { margin-top: 0; }

/* F03.4: Home's Attention-needed/Paused/reopened-step cards — amber like
   every other "needs action soon" surface (.alert.warning uses the same two
   tokens), on top of the ordinary .card so it still gets the shared padding,
   radius and spacing. */
.card.attention { background: var(--warn-bg); border-color: var(--warn); }

/* F03.5/US-03.5.2: Home's three independently-loading sections reserve this
   height whether they're showing a "—" placeholder or filled content, so a
   query that resolves later never shifts the layout by more than a line. */
.home-tile { min-height: 9.5rem; }

/* ---------- Home: setup-state progress card & checklist (F03.2) ---------- */

.setup-step-lede { font-size: 1.1rem; font-weight: 700; color: var(--navy); margin: .25rem 0 .6rem; }

.progress-bar { background: var(--blue-tint); border-radius: 999px; height: .5rem; overflow: hidden; margin: .3rem 0; }
.progress-bar > span { display: block; height: 100%; background: var(--blue); border-radius: 999px; }

/* US-03.2.1: the one primary action while setup runs stays reachable and
   full-width on a phone — 44px is the minimum comfortable tap target. */
.btn.setup-cta { min-height: 44px; }
@media (max-width: 720px) {
    .btn.setup-cta { width: 100%; }
}

/* US-03.2.4: team headcount and role collapse to one muted line under the
   main content in every state — this just keeps its pieces (the count, the
   optional invitation count, the role, "Team", and an error's own retry)
   readable and wrapping cleanly instead of running together. */
.team-line { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: 1rem 0 0; }

/* ---------- Home: Active-state count tiles & top matches (F03.3) ---------- */

/* Three tiles side by side on desktop; each at least 44px tall even before
   its number loads (US-03.3.2's "—" placeholder never collapses the row).
   Below 720px they stack into single-column cards, same breakpoint every
   other responsive rule in this file already uses. */
.count-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: .75rem; }
.count-tiles .tile {
    min-height: 44px; padding: .85rem 1rem; border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--blue-tint);
}
.count-tiles .tile-num { font-size: 2rem; font-weight: 700; color: var(--navy); font-family: var(--head); margin: 0; line-height: 1.1; }
@media (max-width: 720px) {
    .count-tiles { grid-template-columns: 1fr; }
}

/* Each of the top-5 matches renders as its own row — a flex container that
   wraps into a stacked card on a phone instead of a table that would need a
   separate responsive-table trick (US-03.3.2's "rows render as stacked
   cards" at 375px). */
.match-rows { display: flex; flex-direction: column; gap: .5rem; margin-top: .5rem; }
.match-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem;
    padding: .75rem; border: 1px solid var(--line); border-radius: var(--radius);
}
.match-row-main { flex: 1 1 16rem; min-width: 0; }
.match-row-main a { text-decoration: none; }
.match-row-closes { flex: 0 0 auto; }
.match-row-closes.warn { color: var(--warn); font-weight: 600; }

/* F06.3/US-06.3.2: an overdue or due-today pipeline follow-up — the words
   ("— overdue" / "Follow up today") already carry the meaning, so this is
   emphasis, never the only cue. */
.text-warn { color: var(--warn); font-weight: 600; }
.match-row-score { flex: 0 0 auto; display: flex; align-items: center; gap: .4rem; }
.match-row-actions { flex: 1 1 100%; display: flex; align-items: center; gap: .5rem; }
/* US-03.3.5: at least a 44px tap target even at .btn.small's smaller
   font/padding — scoped to this row's own buttons, not every .btn.small
   site-wide (many of those sit in dense table action-cells). */
.match-row-actions .btn { min-height: 44px; }
@media (max-width: 720px) {
    .match-row { flex-direction: column; align-items: stretch; }
    .match-row-actions { flex-direction: column; align-items: stretch; }
}

/* ---------- Briefings: recipient rows (F07.4/US-07.4.4, BRIEF-14) ----------
   Cards, not a table.list, so nothing is ever clipped past the card edge on
   a phone: the address is always its own line, and the state badge plus row
   actions share a second line that wraps naturally at any width — the same
   flex-wrap approach as .match-row above, applied here without needing a
   720px breakpoint of its own since it never depends on a fixed column
   count. */
.recipient-rows { display: flex; flex-direction: column; margin-top: .5rem; }
.recipient-row { padding: .75rem 0; border-bottom: 1px solid var(--line); }
.recipient-row:last-child { border-bottom: none; }
.recipient-row-address { font-weight: 600; }
.recipient-row-secondary {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; margin-top: .35rem;
}
.recipient-row-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
/* US-07.4.4: every row button at least 44px tall AND wide on a phone. */
.recipient-row-actions .btn { min-height: 44px; min-width: 44px; }

/* ---------- Home: "Needs a decision" (F03.3/US-03.3.4) ---------- */

.decision-list { list-style: none; padding: 0; margin: .5rem 0 0; }
.decision-list li { border-bottom: 1px solid var(--line); }
.decision-list li:last-child { border-bottom: 0; }
.decision-list a {
    display: flex; align-items: center; min-height: 44px; padding: .6rem .25rem;
    text-decoration: none; color: inherit;
}
.decision-list a:hover { background: var(--blue-tint); }

/* US-03.2.3: SetupChecklist — each row is itself the link (a single,
   full-height tap target) rather than a small "Open" link at the end, so
   nothing is ever clipped on a phone. */
.setup-checklist { list-style: none; padding: 0; margin: .5rem 0 0; }
.setup-checklist-row { border-bottom: 1px solid var(--line); }
.setup-checklist-row:last-child { border-bottom: 0; }
.setup-checklist-row.next { background: var(--blue-tint); }
.setup-checklist-row a {
    display: flex; align-items: center; gap: .75rem;
    min-height: 44px; padding: .6rem .5rem;
    text-decoration: none; color: inherit;
}
.setup-checklist-row a:hover { background: var(--blue-tint); }
.setup-checklist-row a:focus-visible { outline: 3px solid var(--blue); outline-offset: -3px; }
.setup-checklist-num.badge {
    flex: 0 0 auto; width: 1.6rem; height: 1.6rem; padding: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
}
.setup-checklist-body {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .4rem .75rem;
}
.setup-checklist-name { font-weight: 600; color: var(--ink); }
@media (max-width: 720px) {
    .setup-checklist-row a { flex-direction: column; align-items: flex-start; gap: .35rem; }
    .setup-checklist-body { flex-direction: column; align-items: flex-start; }
}

/* ---------- Setup: numbered checklist & test-run cards (E04/F04.1) ---------- */

.setup-summary { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin: .25rem 0 .75rem; }

.setup-numbered-list { list-style: none; padding: 0; margin: 0; }
.setup-numbered-row {
    display: flex; align-items: center; gap: .75rem;
    padding: .75rem .25rem; border-bottom: 1px solid var(--line);
}
.setup-numbered-row:last-child { border-bottom: 0; }
.setup-numbered-row.next { background: var(--blue-tint); }
.setup-numbered-main { flex: 1 1 auto; min-width: 0; }
.setup-numbered-head { display: block; }
.setup-numbered-reason { display: block; margin-top: .15rem; }
.setup-numbered-row .badge { flex: 0 0 auto; }
.setup-numbered-row .btn { flex: 0 0 auto; min-height: 44px; }

/* US-04.1.3: at 375px each step is a stacked card — number+name first line,
   the full reason wrapped beneath (nothing clipped), the status word, then
   one full-width >=44px button; at 1280x720 the row layout above stays, with
   the button in a right-hand column. */
@media (max-width: 720px) {
    .setup-numbered-row {
        flex-direction: column; align-items: stretch; gap: .4rem;
        padding: .85rem .25rem;
    }
    .setup-numbered-row .btn { width: 100%; }
}

/* US-04.1.3: "Test against real bids" sample cards — title, poster, closes
   date and reasons, then Relevant/Not-relevant as 44px side-by-side buttons
   and "Open the notice" as its own link beneath — never a table that clips
   text or pushes a button off-screen. */
.calibration-samples { display: flex; flex-direction: column; gap: .75rem; margin-top: .5rem; }
.calibration-sample {
    display: flex; flex-wrap: wrap; gap: .5rem 1rem;
    padding: .75rem; border: 1px solid var(--line); border-radius: var(--radius);
}
.calibration-sample-main { flex: 1 1 16rem; min-width: 0; }
.calibration-sample-actions { flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.calibration-sample-actions .btn { min-height: 44px; }
@media (max-width: 720px) {
    .calibration-sample { flex-direction: column; align-items: stretch; }
    .calibration-sample-actions { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
    .calibration-sample-actions a { grid-column: 1 / -1; text-align: center; }
}

/* US-04.1.3: "Confirm and switch on" — a 44px tap area for each checkbox
   (the whole labeled row, not just the tiny square) and a confirmed-by line
   that wraps like any other paragraph; the button reuses .setup-cta, already
   full-width under 720px. */
#switch-on .check-row { min-height: 44px; }

.rule {
    display: flex; align-items: center; gap: .6rem;
    color: var(--blue); margin: .75rem 0 1.25rem;
}
.rule::before, .rule::after {
    content: ""; flex: 1; height: 1px; background: var(--line);
}
.rule .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--blue); }

/* ---------- My agent tab strip (US-02.5.1) ---------- */

.my-agent-tabs {
    display: flex;
    gap: .2rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
    margin: .35rem 0 1.25rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid var(--line);
}
.my-agent-tabs a {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 .85rem;
    text-decoration: none;
    color: var(--ink-2);
    font-weight: 500;
    font-size: .92rem;
    border-bottom: 2px solid transparent;
}
.my-agent-tabs a:hover { background: var(--blue-tint); color: var(--navy); }
.my-agent-tabs a.current {
    font-weight: 700;
    color: var(--navy);
    border-bottom-color: var(--blue-dark);
}

/* ---------- Ask tab strip (F08.6/US-08.6.3) ----------
   Same rules as .my-agent-tabs above — kept as its own class rather than
   reusing that one because these two tabs are not the "My agent" hub (Ask
   has its own top-level nav entry, F02.1), just a second page using the same
   look. */
.ask-tabs {
    display: flex;
    gap: .2rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
    margin: .35rem 0 1.25rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid var(--line);
}
.ask-tabs a {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 .85rem;
    text-decoration: none;
    color: var(--ink-2);
    font-weight: 500;
    font-size: .92rem;
    border-bottom: 2px solid transparent;
}
.ask-tabs a:hover { background: var(--blue-tint); color: var(--navy); }
.ask-tabs a.current {
    font-weight: 700;
    color: var(--navy);
    border-bottom-color: var(--blue-dark);
}

/* ---------- collapsed disclosures (US-02.5.4) ---------- */

details.disclosure { margin: .5rem 0; }
details.disclosure > summary {
    cursor: pointer; font-weight: 600; color: var(--navy-ink); padding: .3rem 0;
}
details.disclosure ul { margin: .5rem 0 .5rem 1.1rem; }
details.disclosure details { margin: .5rem 0 .5rem 1rem; }

/* ---------- NAICS trade-code picker (F04.4) ---------- */

.naics-suggestions { display: flex; flex-direction: column; gap: .4rem; margin: .5rem 0; }
.naics-suggestion-row {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .5rem .7rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.naics-suggestion-row .btn { flex-shrink: 0; }

.naics-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin: .5rem 0; }
.chip {
    display: inline-flex; align-items: center; gap: .4rem;
    background: var(--blue-tint); border: 1px solid var(--line); border-radius: 999px;
    padding: .3rem .5rem .3rem .8rem; font-size: .88rem; color: var(--navy-ink);
}
.chip .chip-check { color: var(--ok); font-weight: 700; }
.chip .chip-remove {
    background: none; border: none; color: var(--muted); cursor: pointer;
    font-size: 1rem; line-height: 1; padding: .2rem .5rem; border-radius: 50%;
    min-width: 1.5rem; min-height: 1.5rem;
}
.chip .chip-remove:hover { background: var(--danger-bg); color: var(--danger); }

.naics-results {
    list-style: none; margin: .25rem 0 0; padding: 0; width: 100%; max-width: 40rem;
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    max-height: 20rem; overflow-y: auto;
}
.naics-results li {
    padding: .55rem .75rem; min-height: 44px; box-sizing: border-box;
    display: flex; align-items: center; cursor: pointer; border-bottom: 1px solid var(--line);
}
.naics-results li:last-child { border-bottom: none; }
.naics-results li:hover, .naics-results li.active { background: var(--blue-tint); }

/* ---------- Admin: searchable customer picker (F15.2/US-15.2.3) ---------- */

/* US-13.8.1 (undefined class): the component's own root — no rule of its own
   (its children already carry every visual property), kept as a real
   selector rather than a bare comment so the "every class used exists" test
   can find it and a future rule has somewhere to go. */
.customer-picker {
}
/* Same accessible combobox/listbox shape as .naics-results (Profile.razor's
   trade-code picker) — kept as its own generic rule set rather than reusing
   that name on an unrelated admin control. */
.picker-results {
    list-style: none; margin: .25rem 0 0; padding: 0; width: 100%; max-width: 30rem;
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    max-height: 20rem; overflow-y: auto;
}
.picker-results li {
    padding: .55rem .75rem; min-height: 44px; box-sizing: border-box;
    display: flex; align-items: center; cursor: pointer; border-bottom: 1px solid var(--line);
}
.picker-results li:last-child { border-bottom: none; }
.picker-results li:hover, .picker-results li.active { background: var(--blue-tint); }

/* ---------- Admin: Today overview tiles (F15.2/US-15.2.1) ---------- */

/* Eight tiles, four across so they fit two rows at 1280px (US-15.2.1); each
   tile IS the link (the whole card is tappable, not just a line inside it),
   at least 44px tall even before its count loads. Two per row at/under
   720px, the same breakpoint every other responsive rule here already uses. */
.today-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: .75rem; }
.today-tiles a.tile {
    min-height: 44px; padding: .85rem 1rem; border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--blue-tint); display: block; text-decoration: none; color: inherit;
}
.today-tiles a.tile:hover { border-color: var(--blue); }
.today-tiles .tile-num { font-size: 2rem; font-weight: 700; color: var(--navy); font-family: var(--head); margin: 0; line-height: 1.1; }
.today-tiles .tile-num.zero, .today-tiles .tile-num.failed { color: var(--muted); }
@media (max-width: 720px) {
    .today-tiles { grid-template-columns: repeat(2, 1fr); }
}

.checklist { list-style: none; padding: 0; margin: .5rem 0; }
.checklist li { padding-left: 2rem; position: relative; margin: .5rem 0; }
.checklist li::before {
    content: "✓";
    position: absolute; left: 0; top: 1px;
    width: 1.35rem; height: 1.35rem; border-radius: 50%;
    background: var(--ok-bg); color: var(--ok);
    font-size: .8rem; font-weight: bold;
    display: flex; align-items: center; justify-content: center;
}

/* ---------- forms & buttons ---------- */

label { display: block; font-weight: 600; margin: .7rem 0 .2rem; color: var(--navy-ink); font-size: .92rem; }
/* :where() keeps this base at zero specificity so the .w-* width utilities
   (and any per-field override) reliably win — including on bare <input> with
   no type attribute, which the old element-only selector missed entirely.
   US-13.2.1 (STY-02): date, password and file join the list here (the same
   change US-13.8.1 needed) so every field in the app gets the same visible
   edge, radius and padding — a field is no longer nearly invisible until it
   is focused. */
:where(input[type=text], input[type=email], input[type=number], input[type=url], input[type=search], input[type=tel], input[type=date], input[type=password], input[type=file], input:not([type]), select, textarea) {
    width: 100%; max-width: 460px;
    padding: .5rem .65rem;
    border: 1px solid var(--field-border); border-radius: var(--radius-sm);
    font-family: var(--sans); font-size: .95rem;
    background: #fbfcfe;
    color: var(--ink);
}
/* US-13.2.1: placeholder text reads as a hint, not a filled-in value — the
   same muted tone the rest of the app already uses for secondary text. */
input::placeholder, textarea::placeholder { color: var(--muted); opacity: 1; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--blue); outline-offset: 1px; border-color: var(--blue); }

.validation-message { color: var(--danger); font-size: .85rem; }
/* US-14.1.4: a field a rejected submission points at carries aria-invalid
   itself (rather than a separate class), so the visible outline and the
   accessible state can never drift apart. US-13.8.2 (STY-11/dead CSS):
   removed the older .valid.modified/.invalid pair here — Blazor only ever
   applies those classes to an <InputBase> inside an <EditForm>, and this
   app has neither; they had zero effect on anything. */
input[aria-invalid=true], select[aria-invalid=true], textarea[aria-invalid=true] {
    outline: 2px solid var(--danger); border-color: var(--danger);
}

/* One checkbox+label idiom for the whole app. */
.check-row {
    display: flex; align-items: center; gap: .5rem;
    font-weight: 400; margin: .5rem 0;
}
.check-row input[type=checkbox] { width: auto; accent-color: var(--blue); }
input[type=checkbox], input[type=radio] { accent-color: var(--blue); }

/* Inline control rows (filters, quick-add forms): controls sit on a shared
   baseline with breathing room, and wrap instead of jamming. */
.control-row {
    display: flex; flex-wrap: wrap; align-items: end; gap: .5rem .75rem;
    margin: .5rem 0;
}
.control-row label { margin: 0 0 .2rem; }
.control-row .field { display: flex; flex-direction: column; }
.control-row .btn { margin-bottom: 1px; }

/* US-05.8.4 (MAT-16): Matches' filter bar is a <fieldset> (a <legend> names
   the group for assistive tech) with the browser's own border/margin/padding
   stripped, so it reads exactly like the plain .card it already sits inside. */
fieldset.filter-fieldset { border: none; margin: 0; padding: 0; }
fieldset.filter-fieldset legend { padding: 0; font-weight: 700; margin-bottom: .5rem; }

/* F05.1 (US-05.1.1/US-05.1.2): the one status line above Matches' bands, with
   its "Filter & sort" toggle at the right end. */
.matches-status-line {
    display: flex; flex-wrap: wrap; justify-content: space-between;
    align-items: center; gap: .5rem 1rem; margin: .5rem 0 1rem;
}
.matches-status-line .status-text { margin: 0; }
/* F05.3/US-05.3.3: the status text and the "Closing soon" toggle chip sit
   together as one flex item, so space-between still only splits the line
   into "everything about the list" vs. the Filter & sort toggle. */
.matches-status-line .status-line-main {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem;
}

/* US-05.5.2/US-05.5.3: however many counts StatusLineText() joins with "·",
   the line itself never grows past two on a phone — anything past that
   truncates with an ellipsis instead of pushing the first band further down
   the screen (MAT-09's 560px target). Unchanged above 720px, where the line
   already fits on one. */
@media (max-width: 720px) {
    .matches-status-line .status-text {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
}

/* F05.3/US-05.3.3: a pill-shaped toggle button — filled when it's on,
   outlined when off, matching the chip look the mock-up uses for the other
   list-filtering chips this epic adds later (F05.7/F05.8). */
.chip-toggle {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .3rem .85rem; border-radius: 999px; border: 1px solid var(--navy);
    background: #fff; color: var(--navy); font: inherit; font-weight: 600;
    font-size: .82rem; line-height: 1.2; cursor: pointer; min-height: 32px;
    white-space: nowrap;
}
.chip-toggle:hover:not(:disabled) { background: var(--blue-tint); }
.chip-toggle:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.chip-toggle.active { background: var(--navy); color: #fff; }
.chip-toggle:disabled { opacity: .5; cursor: not-allowed; }

/* F05.3/US-05.3.2: the deadline chip beside the Fit pill. Colour carries the
   urgency, but every level (including neutral) gets the same clock icon, so
   colour is never the only cue (WCAG 1.4.1); "Closed" reuses the plain grey
   .badge instead of a fourth colour (US-05.3.4). */
.deadline-chip {
    display: inline-flex; align-items: center; gap: .3rem;
    font-weight: 600; font-size: .88rem; white-space: nowrap;
}
.deadline-chip .clock-icon { width: 14px; height: 14px; flex: 0 0 auto; }
.deadline-chip.urgency-red { color: var(--danger); }
.deadline-chip.urgency-amber { color: var(--warn); }
.deadline-chip.urgency-neutral { color: var(--ink-2); }
.deadline-chip.urgency-none { color: var(--muted); font-weight: 500; }

/* F05.3/US-05.3.1: "Why it fits" reads as a text disclosure under the row's
   first two reasons, not a boxed button — its label never flips to "Hide"
   when open (that stays a job for aria-expanded). */
.why-it-fits-toggle {
    display: inline-block; background: none; border: none; padding: 0;
    margin: .3rem 0 0; font: inherit; font-weight: 600; font-size: .85rem;
    color: var(--blue-dark); text-decoration: underline; cursor: pointer;
}
.why-it-fits-toggle:hover { color: var(--navy); }
.why-it-fits-toggle:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

/* F05.5/US-05.5.3: a band heading is itself the fold/unfold control for every
   band except "Look at these first" (which stays the plain h2 text just
   above this rule's reach — MAT-09 means that one is never a tap away from
   view). font/color:inherit pulls the surrounding h2's own Quicksand/700/
   navy-deep styling, so swapping the heading text for a button changes
   nothing visually except the pointer and the chevron. */
.band-toggle {
    display: inline-flex; align-items: center; gap: .5rem;
    background: none; border: none; padding: 0; margin: 0;
    font: inherit; color: inherit; cursor: pointer; min-height: 44px;
}
.band-toggle:hover { color: var(--navy); }
.band-toggle:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.band-toggle-icon { font-size: .8rem; color: var(--muted); }

/* A button styled as an inline text link — "Undo" inside the collapsed
   hidden-row strip (F05.2/US-05.2.2), and anywhere else a same-line action
   word reads better than a boxed button. */
.link-button {
    display: inline; background: none; border: none; padding: 0;
    font: inherit; font-weight: 600; color: var(--blue-dark);
    text-decoration: underline; cursor: pointer;
}
.link-button:hover { color: var(--navy); }
.link-button:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.link-button:disabled { opacity: .55; cursor: not-allowed; text-decoration: none; }

/* F05.2/US-05.2.2: "Not relevant" collapses the whole row to this one-line
   grey strip in place — title truncated to one line (no CSS "cards" exist
   yet for Matches; the strip works the same inside the existing table row at
   every width, including the .table-scroll phone stopgap). */
.hidden-row td { background: var(--blue-tint); display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.hidden-row-title {
    flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; font-weight: 600; color: var(--muted);
}

/* F05.6/US-05.6.1-2: the single-bid page's fact strip — a wrapping row of
   label/value pairs inside one .card, and its one action row. */
.opp-facts { display: flex; flex-wrap: wrap; gap: 1rem 2rem; }
.opp-fact { min-width: 9rem; }
@media (max-width: 480px) {
    /* US-05.6.1: full width instead of a row of unevenly sized buttons — the
       same "no sideways scroll" goal US-05.5.1 already gives Matches. */
    .opp-actions .btn { width: 100%; }
}

/* US-05.10.90/EXP-03: the Expansion Advisor card's two actions, sized like
   every other row action rather than the smaller default .btn. */
.expansion-actions .btn { min-height: 44px; }
/* US-13.8.2 (STY-14): the recommendation's headline sentence ("could win N
   more bids a year") — was a one-off inline style; kept at its own size and
   colour (not the generic .stat scale below, which nothing here calls for)
   so converting it changes no pixel. */
.rec-headline { font-size: 1.15rem; font-weight: 600; margin: .35rem 0; color: var(--navy); }

/* F05.1: a disclosure region that's always in the DOM — so its trigger's
   aria-controls resolves to a real element whichever way it opens — and
   toggled purely by this class; display:none already keeps a collapsed
   region's controls out of the tab order, so there's no separate
   bookkeeping (the same pattern MobileMenu's drawer uses). Used by the
   filter bar (US-05.1.2) and the run-details expander (US-05.1.3). */
.collapsible { display: none; }
.collapsible.open { display: block; }

/* F05.1 (US-05.1.3): the foot-of-list last-check line, its "Check for new
   bids now" link and the Details toggle share one wrapping row. */
.matches-foot-line {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem;
}

/* Field width scale — replaces per-page max-width hacks. */
.w-xs { max-width: 6rem; }
.w-sm { max-width: 10rem; }
.w-md { max-width: 16rem; }
.w-lg { max-width: 24rem; }
/* Long single-line values (URLs, legal names, headlines) and roomy prose
   boxes — wider than the 460px default. */
.w-xl { max-width: 40rem; }
/* US-13.8.2 (dead CSS): .w-full { max-width: 100% } was removed here — no
   field in the app ever used it; a bare field already fills its container
   up to the base selector's own 460px cap (see the Matches/Pipeline search
   fields, which used to fight that cap with .w-sm instead). */

/* Multi-line inputs can be dragged taller but never wider (keeps layouts). */
textarea { resize: vertical; }

/* US-13.1.1 (STY-06): the primary button's resting state is --blue-dark
   (#0a76cc), not --blue (#0d94fe) — white text on it measures 4.69:1 (AA),
   where the marketing --blue only reaches 3.14:1. --blue is kept for
   :hover/:focus-visible (a transient state, not the resting one this
   criterion targets) plus .rule, .card.feature and the support banner
   border, so the shared marketing palette is otherwise unchanged — see
   docs/brand.md. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1.25;
    background: var(--blue-dark);
    color: #fff !important;
    border: 2px solid var(--blue-dark);
    border-radius: 10px;
    font-family: var(--sans);
    font-weight: 600;
    font-size: .95rem;
    padding: .45rem 1.1rem;
    cursor: pointer;
    text-decoration: none;
    transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { background: var(--blue); border-color: var(--blue); }
.btn:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }
.btn.secondary { background: #fff; color: var(--navy) !important; border-color: var(--navy); }
.btn.secondary:hover { background: var(--blue-tint); }
.btn.danger { background: var(--danger); border-color: var(--danger); }
.btn.danger:hover { background: #a10009; border-color: #a10009; }
/* US-13.4.2 (STY-05): .8rem (12.8px) raised to .875rem (14px) — a row's
   Save/Details/Remove label is body-adjacent text customers read constantly,
   not fine print. */
.btn.small { font-size: .875rem; padding: .32rem .7rem; border-width: 1px; }

/* US-13.3.2 (STY-03/STY-20): Windows High Contrast flattens .btn and
   .btn.danger to the same system button face — this keeps the destructive
   one visually distinct by shape, not colour, once forced colours are on. */
@media (forced-colors: active) {
    .btn.danger { border: 2px dashed; }
}

/* US-13.3.1/US-13.3.3 (STY-03): every button, nav link and the header's
   Sign in control reach a real 44px touch target on any touch-capable
   device — keyed off touch capability (pointer: coarse) rather than a
   max-width breakpoint, since a touch tablet or a resized touch laptop can
   be wider than any phone breakpoint and still needs a thumb-sized target.
   Desktop with a mouse keeps .btn.small's current compact ~28px height. */
@media (pointer: coarse) {
    .btn, .btn.small, .userbox .btn { min-height: 44px; }
    .topbar nav a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}

/* US-13.1.2 (STY-06): an explicit disabled treatment instead of 55% opacity,
   which left white .btn text at about 1.9:1 once composited on white. Text
   at #4b5872 on #e8edf5 measures 6.1:1; the rule needs !important on colour
   only because .btn/.btn.secondary already set colour !important for their
   enabled state and a plain :disabled override cannot otherwise win. Applies
   to every .btn variant (.small/.secondary/.danger all carry the base .btn
   class in markup, so one selector list covers them); a disabled button is
   never the only place a reason lives (that stays text beside it, per
   US-13.1.2's own note — an E09 copy concern, not this rule's job). */
.btn:disabled,
.btn.small:disabled,
.btn.secondary:disabled,
.btn.danger:disabled {
    background: #e8edf5;
    color: #4b5872 !important;
    border-color: #cfd7e3;
    cursor: not-allowed;
}
button:disabled { cursor: not-allowed; }

/* Button groups: consistent gap, aligned heights, wraps on narrow screens. */
.actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
    margin-top: .75rem;
}
.actions.tight { margin-top: .35rem; }

/* ---------- documents (F12.1) ---------- */

/* US-12.1.4: the drop zone is the upload control's whole container — the
   InputFile's native <input type=file> is visually hidden (.sr-only below)
   and triggered by the "Choose a file" label/button instead, so a browser's
   inconsistent native file-input skin never shows. Documents.razor.js adds
   nothing beyond a real browser drag-and-drop: it points the dropped
   FileList at that same hidden input and fires the 'change' event InputFile
   already listens for. */
.dropzone {
    border: 1.5px dashed #9aa8c2;
    border-radius: var(--radius-sm);
    background: var(--blue-tint);
    padding: 1.1rem;
    text-align: center;
    margin: .5rem 0 .75rem;
}
.dropzone.dropzone-active { border-color: var(--blue); background: var(--blue-soft); }
.dropzone input[type=file] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.dropzone .btn { margin-top: .5rem; }

/* US-12.1.1: the ordered "start with these" checklist that replaces the
   plain empty state once at least one material exists to read it against —
   normal (non-italic) weight, unlike .empty-state, because it is guidance to
   act on rather than a placeholder. */
.materials-guide ol { margin: .5rem 0 .5rem 1.2rem; padding: 0; }
.materials-guide li { margin: .3rem 0; }

/* ---------- utility layer ---------- */

.row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: .5rem; }
.mt-2 { margin-top: .75rem; }
.mt-3 { margin-top: 1.25rem; }
/* US-13.8.2 (STY-14): the AC asked for a full .mb-1/.mb-2/.mb-3 set mirroring
   .mt-1/.mt-2/.mt-3, but only one inline `margin-bottom` style exists in the
   current codebase (Profile.razor's section heading) — adding the other two
   tiers with no caller would just be new dead CSS in the same PR that
   removes it elsewhere. Only the tier this pass actually uses is added; the
   scale can grow the next time a page needs "the same gap, below instead of
   above" at a different size. */
.mb-1 { margin-bottom: .5rem; }
.nowrap { white-space: nowrap; }
.text-danger { color: var(--danger); }
/* US-13.8.1 (undefined class): for text that needs <strong>'s emphasis on an
   element <strong> itself can't be (an <a>, a <p> that's already inside its
   own strong-adjacent markup) — Dashboard's top-match title link, the
   customer picker's "Customer: {name}" line. */
.strong { font-weight: 700; }
.empty-state { color: var(--muted); font-size: .9rem; font-style: italic; margin: .75rem 0; }

/* ---------- US-13.8.2 (STY-14): inline styles retired to named utilities ----------
   Thirty-plus one-off `style="..."` attributes patched gaps this layer never
   had — a flush heading, a narrow sub-list, small print, a field that should
   grow, a button pinned to the far end of its row. Named once here so the
   next page reaches for a class instead of copying a hack; each replaces
   the exact declarations the inline style carried, so converting a page to
   one of these is a no-op visually. */
.h-flush { margin: 0; }
/* The same small top nudge with the bottom margin explicitly zeroed — for a
   line that sits directly under a heading with nothing of its own below it
   (Agent.razor's status line), where .h-flush's full margin:0 would also
   swallow the spacing a plain .mt-1/.mt-2 expects to add underneath. */
.mt-tight { margin: .15rem 0 0; }
.sub-list { margin: .3rem 0 0 1rem; }
.pre-wrap { white-space: pre-wrap; }
.text-sm { font-size: .85rem; }
.text-center { text-align: center; }
.no-underline { text-decoration: none; }
.ml-auto { margin-left: auto; }
.mr-1 { margin-right: .35rem; }
.flex-1 { flex: 1; }
/* The label/help-text pair under a control (Onboarding's "Short company
   name", Settings' display name and time zone, Pipeline's follow-up date):
   a small gap above, a little more below before the field it describes. */
.field-help { margin: .15rem 0 .35rem; }
/* A filter-row search field that should claim the row's spare width instead
   of sitting at its default column size (Matches, Pipeline). */
.field-grow { flex: 1 1 260px; }
/* The uploaded agent-logo preview: card-header size (Agent.razor) versus the
   smaller inline mark beside a heading (Dashboard.razor) — two fixed spots,
   not a generic scale. */
.agent-logo-preview { max-height: 64px; max-width: 120px; }
.agent-logo-inline { max-height: 28px; vertical-align: middle; margin-right: .4rem; }
/* Home's un-hero'd lead paragraph — .hero p.lead (below) only ever matches
   inside the marketing hero, so a lead sentence placed elsewhere (the "why
   this works" section here) needs its own width and centering. */
.page-intro { max-width: 760px; margin: 0 auto 2rem; }
/* AdminQueues' possible-duplicate review card, and the two-column
   standardized/raw panel each one opens into. */
.dup-case { border: 1px solid var(--line); border-radius: 6px; padding: .6rem; margin: .5rem 0; }
.raw-record { max-height: 220px; overflow: auto; font-size: .7rem; background: var(--bg); padding: .5rem; }
/* Briefings' two read-only "what the email looks like" frames: the compact
   one inside a Digest-history row, and the phone/desktop toggle on Preview
   next briefing — sized by a class instead of an inline max-width driven by
   the toggle's own state. Named apart from the unrelated .email-preview
   iframe below (AdminCampaigns' rendered-email preview) so the two rules
   never merge onto the same element. */
.sent-email-preview { border: 1px solid var(--line); padding: 8px; max-height: 420px; overflow: auto; pointer-events: none; }
.preview-frame { border: 1px solid var(--line); padding: 12px; margin: 0 auto; max-height: 640px; overflow: auto; pointer-events: none; }
.preview-frame.phone { max-width: 375px; }
.preview-frame.desktop { max-width: 640px; }

/* US-05.4.1: three plain grey placeholder rows while Matches' Reload() is in
   flight — a static shape (no shimmer/motion, so there's nothing to suppress
   for prefers-reduced-motion), never mistaken for the real .empty-state
   above since the two never render at the same time. */
.skeleton-row {
    height: 2.5rem;
    border-radius: var(--radius-sm);
    background: var(--blue-tint);
    border: 1px solid var(--line);
    margin-top: .5rem;
}

/* ---------- tables ---------- */

/* US-13.6.3 (F13.6, supersedes the F05.5/US-05.5.1 stopgap): every
   table.list now stacks into cards under 720px by default (the rule below,
   generalized from F14.5's directory-cards) — .table-scroll is the explicit
   opt-OUT for the rare table that must stay tabular (a wide operator table
   under /admin, or any other table.list whose columns genuinely cannot
   stack), so it scrolls inside its own card instead of stacking, and instead
   of forcing the whole page to scroll sideways. tabindex="0" plus the
   focus-visible ring below (US-13.6.3) let a keyboard user actually reach
   and scroll it. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll:focus-visible { outline: 3px solid var(--blue); outline-offset: -3px; }

table.list { width: 100%; border-collapse: collapse; margin-top: .5rem; }
/* US-13.4.2 (STY-05): sentence case, no letter-spacing tracking — uppercase
   column headers read slower and can trip some screen readers' pronunciation
   (spelling out short strings instead of reading the word). Text itself was
   already sentence case in every page's markup (e.g. "Fit", "Closes",
   "What changed"); removing the transform just stops overriding it. .78rem
   (12.5px) is raised to .85rem for the same reason as the other secondary
   text in this story. */
table.list th {
    text-align: left; font-size: .85rem;
    color: var(--muted); border-bottom: 2px solid var(--blue);
    padding: .45rem .6rem;
}
table.list td { padding: .55rem .6rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
table.list tr:hover td { background: var(--blue-tint); }

/* F08.7/US-08.7.3: the /admin/conversations period total row — a plain top
   rule instead of the hover/bottom-border treatment rows get, so it reads as
   a summary rather than one more company. */
table.list tfoot td { border-bottom: none; border-top: 2px solid var(--line); }
/* US-13.3.1 (STY-03): gap-based spacing keeps at least 8px between adjacent
   row buttons and lets a button that doesn't fit wrap onto its own line
   instead of forcing the row (or the whole table) to scroll sideways;
   desktop still fits everything on one line wherever there is room, since a
   flex row only wraps when it must. Applies at every width — this used to
   be a max-width:720px stopgap scoped to .table-scroll (see below), which
   this supersedes.
   row-gap is 1rem rather than the same .5rem as column-gap: on a page that
   has no F13.6 stacked-card layout of its own (Pipeline, Documents, Team),
   a narrow phone can still squeeze this cell enough that its buttons wrap
   onto separate lines — measured with device emulation at 375px — and at
   that point .btn.danger's margin-left:auto below (a same-line, horizontal
   push) has no following space left to absorb. The taller row-gap is what
   still guarantees US-13.3.2's 16px clear space above the danger button in
   that wrapped case, with no effect on the shared-single-line case (1280px,
   and 375px wherever the row already fits) since row-gap never applies
   within one line. */
table.list td.actions-cell {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: .5rem;
    row-gap: 1rem;
}
/* US-13.3.2 (STY-03/STY-20): the one truly destructive action in a row, if
   any, sits at the row's own end with real clear space from whatever comes
   before it — never immediately beside the button a customer actually
   wants (STY-03's "5-10px from the primary"). Every current .btn.danger
   inside an actions-cell (Pipeline Remove, Documents Delete, Team's invite
   Cancel, and the admin console's own row actions) is already the last
   button in its cell, so this is a pure placement fix with no markup
   change. */
table.list td.actions-cell .btn.danger { margin-left: auto; }

/* US-14.3.2: /admin/campaigns' rendered-email preview — a real iframe so the
   frame's own inline styles (email-safe, not app.css) render exactly as a
   recipient's inbox would show them. */
.email-preview {
    display: block; width: 100%; max-width: 640px; height: 520px;
    border: 1px solid var(--line); border-radius: 8px; background: #fff;
}

/* US-11.3.2 (TEAM-04): the "couldn't copy automatically" fallback — a
   read-only link right where the Copy link button already is, instead of an
   unhandled-error bar with nothing on screen to copy by hand. */
table.list td.actions-cell .copy-fallback { white-space: normal; margin-bottom: .4rem; }
table.list td.actions-cell .copy-fallback input { width: 100%; }

/* US-13.3.1 (F05.5's original US-05.5.1 stopgap, now superseded): the
   .table-scroll-scoped, max-width:720px-only version of this same
   wrap/gap/44px rule is gone — table.list td.actions-cell above already
   applies flex-wrap and an 8px gap at every width, and the pointer:coarse
   rule above already gives every .btn its 44px floor on any touch device
   regardless of viewport width, so keeping both was two copies of the same
   rule that could only drift apart. */

/* ---------- US-13.6.1/US-05.5.2: Matches' own band table becomes a stacked
   card on a phone ----------
   Still scoped to table.list.match-cards only, not widened to every
   table.list by the generic rule below — Matches' Fit/Closes pair shares one
   visual row inside the card (a six-area grid), which the generic rule's
   plain top-to-bottom stack (one field per line) does not attempt. Every
   other table.list (Pipeline, Team, Documents, Profile, Briefings, Coverage,
   Support, the coverage directory, and Matches' own two smaller "Recently
   hidden"/"Removed by your settings" tables) uses the plain generic rule
   instead — table.list.match-cards being a class on the <table>, not a
   different element, means it is simply more specific than the generic
   table.list rule below and wins for Matches' band tables without either
   rule needing to know about the other.

   The technique: at this width the <tr> itself becomes the card (a CSS
   grid) and every <td> gets display:contents, so a <td>'s own CHILDREN —
   never the <td> itself — become the grid's direct items. That's what lets
   a piece of one <td> (the Fit pill) share a visual row with a piece of a
   different <td> (the Closes chip) without touching the underlying
   desktop-table markup or its data (US-05.5.2's own "the card and the
   desktop table are the same markup ... so the two layouts never show
   different data" rule) — the header row is simply hidden at this width,
   since a card's fields are already self-labelled (the Fit pill's own
   number, the deadline chip's own wording), same reasoning F05.1 already
   used to drop the old first-person briefing summary. */
@media (max-width: 720px) {
    table.list.match-cards { display: block; width: 100%; }
    table.list.match-cards thead { display: none; }
    table.list.match-cards tbody { display: block; }

    table.list.match-cards tbody tr:not(.hidden-row) {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-template-areas:
            "title   title"
            "agency  agency"
            "reason  reason"
            "fit     closes"
            "actions actions"
            "why     why";
        align-items: start;
        gap: .5rem;
        overflow-wrap: anywhere;
        background: var(--card);
        border: 1px solid var(--line);
        border-radius: var(--radius);
        box-shadow: var(--shadow);
        padding: 1rem;
        margin-bottom: .75rem;
    }
    /* Both rules needed: the general one places title/fit/closes (never
       touched by the US-05.5.1 stopgap above), and the actions-cell-specific
       one matches that stopgap rule's own specificity so this one — coming
       later in the file — is the one that actually wins for it. */
    table.list.match-cards td { display: contents; }
    table.list.match-cards td.actions-cell { display: contents; }

    table.list.match-cards .mc-title { grid-area: title; }
    table.list.match-cards .mc-agency { grid-area: agency; }
    table.list.match-cards .mc-reason { grid-area: reason; }
    table.list.match-cards .mc-fit { grid-area: fit; justify-self: start; }
    table.list.match-cards .mc-closes { grid-area: closes; justify-self: end; }
    table.list.match-cards .mc-actions { grid-area: actions; }
    table.list.match-cards .mc-why { grid-area: why; margin-top: .25rem; }

    /* US-05.5.2: Save (primary, left) and Not relevant (secondary, right),
       each >=44px tall and >=48% of the card width with >=8px between —
       calc(50% - .25rem) with this row's own .5rem (8px) gap adds up to
       exactly the card's full width, so neither button ever drops under 48%
       the way a plain "48% + 48% + 8px gap" would once flex-shrink resolved
       the overflow that leaves. */
    table.list.match-cards .mc-actions {
        display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
    }
    table.list.match-cards .mc-actions .btn { min-height: 44px; }
    table.list.match-cards .mc-action-btn { flex: 0 1 calc(50% - .25rem); }

    /* The collapsed "Not relevant" strip (F05.2/US-05.2.2) never had a
       Fit/Closes/actions split to place — it keeps its own one-line flex
       treatment instead of the six-area grid above. */
    table.list.match-cards tbody tr.hidden-row {
        display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
        background: var(--blue-tint); border-radius: var(--radius);
        padding: .75rem 1rem; margin-bottom: .75rem;
    }
    table.list.match-cards tbody tr.hidden-row td { display: contents; }

    /* US-05.8.3: bulk selection is a desktop/keyboard convenience — hidden at
       card width rather than given its own grid area, so the six-area layout
       above is unaffected. More specific than the plain "td { display:
       contents }" rule two lines up, so this wins for the checkbox column. */
    table.list.match-cards td.check-col,
    table.list.match-cards th.check-col { display: none; }
}

/* ---------- US-13.6.2 (F13.6): every table.list stacks into cards on a
   phone, with no per-page CSS ----------
   Generalized, unchanged, from F14.5's table.list.directory-cards (the
   coverage directory) — the same declarations, just widened from that one
   page's own class to every table.list under 720px, per this story's own
   plan. Nothing here needs to share a visual row with a sibling cell (unlike
   Matches' match-cards grid above), so each field just stacks top to bottom,
   self-labelled via its own data-label attribute since the header row (its
   only other source for that label) is hidden at this width; a cell with no
   data-label (the actions cell, by convention) shows no label. table.list.
   match-cards above stays more specific and wins for Matches' band tables;
   .table-scroll below stays more specific and wins for the rare table that
   opts out of stacking entirely (US-13.6.3). */
@media (max-width: 720px) {
    table.list, table.list tbody { display: block; width: 100%; }
    table.list thead { display: none; }
    table.list tr {
        display: block;
        background: var(--card);
        border: 1px solid var(--line);
        border-radius: var(--radius);
        box-shadow: var(--shadow);
        padding: 1rem;
        margin-bottom: .75rem;
    }
    table.list tr:hover td { background: none; }
    table.list td { display: block; border-bottom: none; padding: .2rem 0; }
    table.list td[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
        color: var(--muted);
    }

    /* US-13.6.2 (mirrors match-cards' own US-05.8.3 rule): bulk selection is
       a desktop/keyboard convenience, hidden at card width rather than
       given its own line — today only Matches' and Profile's suggestions
       tables have a check-col, and Matches' own is already handled (more
       specifically) by match-cards above. */
    table.list td.check-col, table.list th.check-col { display: none; }

    /* AuditTrail's expand/collapse burst rows (.audit-burst-rows, toggled by
       .open) rely on tbody's default display:none/table-row-group — a plain
       table.list tbody { display: block } above is MORE specific than that
       bare class selector and would otherwise win and always show a
       collapsed burst. These two rules, one class more specific again, take
       it back. */
    table.list tbody.audit-burst-rows { display: none; }
    table.list tbody.audit-burst-rows.open { display: block; }

    /* US-13.6.3: the opt-out. A table.list wrapped in .table-scroll (30
       operator tables under /admin, per that story) stays a real table and
       scrolls horizontally inside its own card instead — every declaration
       above is undone here, at higher specificity (an extra .table-scroll
       class), rather than the generic rule needing to know which tables opt
       out.
       :not(.match-cards) throughout: Matches' own band panels sit inside a
       .table-scroll div too — for the width range ABOVE 720px, where match-
       cards' own @media(max-width:720px) block does not apply and this is
       what keeps a still-wide table from forcing the page to scroll. Without
       :not(.match-cards), this opt-out and table.list.match-cards's own
       rules would have IDENTICAL specificity (two classes, one element) at
       ≤720px, and this block, coming later in the file, would win the tie
       and wrongly un-stack Matches' cards. table.list.match-cards is
       otherwise untouched by, and unaware of, this opt-out. */
    .table-scroll table.list:not(.match-cards) { display: table; width: auto; }
    .table-scroll table.list:not(.match-cards) tbody { display: table-row-group; }
    .table-scroll table.list:not(.match-cards) thead { display: table-header-group; }
    .table-scroll table.list:not(.match-cards) tr {
        display: table-row; background: none; border: none; border-radius: 0;
        box-shadow: none; padding: 0; margin-bottom: 0;
    }
    .table-scroll table.list:not(.match-cards) tr:hover td { background: var(--blue-tint); }
    .table-scroll table.list:not(.match-cards) td {
        display: table-cell; border-bottom: 1px solid var(--line); padding: .55rem .6rem;
    }
    .table-scroll table.list:not(.match-cards) td.check-col,
    .table-scroll table.list:not(.match-cards) th.check-col { display: table-cell; }
}

/* Bulk selection: a narrow checkbox gutter, and the action bar that appears
   once something is ticked. The bar sits above the table so the count and the
   buttons stay in view while scanning a long queue. */
table.list th.check-col, table.list td.check-col { width: 2.75rem; padding: .25rem; text-align: center; }
/* US-13.3.3 (STY-03): a native checkbox renders at about 13px — sized up to
   1.25rem, wrapped in a label (Profile.razor, Matches.razor) so the whole
   padded cell is the tap target that toggles it, not just the small square. */
table.list th.check-col label.check-col-hit, table.list td.check-col label.check-col-hit {
    display: flex; align-items: center; justify-content: center;
    width: 100%; min-height: 44px; margin: 0; cursor: pointer;
}
table.list th.check-col input, table.list td.check-col input {
    width: 1.25rem; height: 1.25rem; margin: 0; cursor: pointer;
}
.bulk-actions { margin-top: .5rem; }

/* ---------- badges & alerts ---------- */

/* US-13.4.2 (STY-05): .72rem (11.5px) raised to .8rem — a status pill (fit
   score band, PREVIEW, a pipeline stage) is never the smallest text on the
   page it appears on. */
.badge {
    display: inline-block; border-radius: 999px; font-size: .8rem; font-weight: 700;
    padding: .15rem .6rem; letter-spacing: .03em;
}
/* US-12.4.2/PROF-03: two adjacent badges (e.g. a section badge followed by
   "owner approves") must never run together as one word. */
.badge + .badge { margin-left: .35rem; }
.badge.navy { background: var(--navy); color: #fff; }
.badge.gold { background: var(--blue-dark); color: #fff; }   /* the accent badge; "gold" is its legacy name. US-13.1.1: --blue-dark for the same 4.69:1 reason as .btn above. */
.badge.outline { border: 1px solid var(--navy); color: var(--navy); }
.badge.gray { background: #e8edf5; color: #4b5872; }
.badge.amber { background: var(--warn-bg); color: var(--warn); } /* F06.2/US-06.2.2: the "Closes in N days" (N<=7) deadline-state badge */
.badge.green { background: var(--ok); color: #fff; }
.badge.red { background: var(--danger); color: #fff; }
.badge.danger { background: var(--danger); color: #fff; }
/* F06.5/US-06.5.3: the "Changed <date>" chip on a pipeline row — a link, not
   just a badge, so it can jump straight to its line in the Changes card. */
.badge.amber { background: var(--warn-bg); color: var(--warn); text-decoration: none; }
.badge.amber:hover { text-decoration: underline; }
.badge.amber:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }

/* F06.5/US-06.5.1 (PIPE-01): the "closes <date>" text once the source itself
   has stopped actively listing the notice — still legible, visibly duller
   than an open row's date. */
.source-closed-date { opacity: .7; }

.alert {
    border-radius: 10px; padding: .7rem 1rem; margin: .75rem 0; font-size: .92rem;
}
.alert.error { background: var(--danger-bg); border: 1px solid var(--danger); color: var(--danger); }
.alert.info { background: var(--blue-soft); border: 1px solid var(--blue); color: var(--navy-ink); }
/* US-13.1.1 (STY-06): #14833b (--ok) on --ok-bg is only 4.3:1 — the border
   keeps --ok, but the text darkens to #0f6b30 (5.9:1) so "Saved to your
   pipeline" and every other success notice reads at AA. */
.alert.success { background: var(--ok-bg); border: 1px solid var(--ok); color: #0f6b30; }
.alert.warning { background: var(--warn-bg); border: 1px solid var(--warn); color: var(--navy-ink); }
/* US-12.1.5: the "Review them →"/"profile page" links that show up inside an
   alert (StatusMessages' ActionLabel/ActionHref, and NoticeContent's inline
   links) get a crisp ring instead of relying on each browser's own default. */
.alert a:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.notice-action { font-weight: 600; }

/* ---------- F10.1: shared status/notice pattern (StatusMessages.razor) ----------
   US-10.1.1: both alert slots stay mounted in the DOM even when empty — role
   and aria-live are on the element from its first paint, never added
   together with the text that needs announcing — so :empty just keeps an
   unused slot out of the page's layout instead of removing it outright.
   US-10.1.3: at 720px and below a NOTICE (never an error, which always stays
   inline beside the control that raised it) becomes a fixed bottom toast
   above the bottom tab bar and the safe area; StatusMessages.razor.js adds
   .toast-dismissed for the 5-second auto-dismiss and the tap-to-dismiss it
   wires once per element — the SAME role="status" element, so nothing is
   ever announced twice. A MobileOnly instance (a row result that already
   answered above this width, e.g. Matches' Save/Not relevant) renders
   nothing at all once the toast rule below stops applying. */
.status-messages .alert:empty { display: none; margin: 0; padding: 0; border: 0; }
.status-messages.mobile-only .alert.error { display: none; }
/* F13.5/US-13.5.2: same rule as h1:focus above — this tabindex="-1" is only
   ever landed on programmatically (StatusMessages.razor.js), never by
   tabbing, so it needs no visible ring of its own; the alert's own colour
   change is the visible cue. */
.status-messages .alert:focus { outline: none; }

@media (max-width: 720px) {
    .status-messages .alert.success:not(:empty) {
        position: fixed;
        left: 16px; right: 16px;
        bottom: calc(64px + env(safe-area-inset-bottom) + 12px);
        z-index: 1155; /* above the bottom tab bar (1150), below the mobile drawer/reconnect dialog (1190+) */
        margin: 0;
        box-shadow: var(--shadow-lg);
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        animation: status-toast-in .2s ease-out;
        cursor: pointer;
    }
    .status-messages .alert.success.toast-dismissed { display: none; }
    .status-messages .alert.success .notice-action {
        display: inline-flex; align-items: center;
        min-height: 44px; padding: 0 .25rem; margin-left: .25rem;
    }
}

@media (min-width: 721px) {
    .status-messages.mobile-only .alert.success { display: none; }
}

@keyframes status-toast-in {
    from { transform: translateY(12px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .status-messages .alert.success { animation: none; }
}

/* Visually hidden but still in the accessibility tree and tab order — for a
   label whose text a sighted user already gets from an adjacent visible
   control (US-12.1.4's "Upload a document" label on the hidden file input). */
.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;
}

/* F10.2/US-10.2.1: Shared/ConfirmButton's own rendered state — an inline
   confirm that replaces the trigger button ("Remove/Delete "{label}"? …
   Yes/Keep") rather than a browser confirm() dialog. white-space: normal
   overrides the nowrap most callers sit inside (table.list's own
   .actions-cell) so the question wraps instead of forcing the row to scroll
   sideways; both buttons get a real 44px tap target on a phone. */
.confirm-inline { white-space: normal; }
.confirm-inline .actions .btn { min-height: 44px; }
/* US-13.8.1 (undefined class): marks Pipeline's past-deadline "did you win?"
   prompt specifically (as opposed to the source-closed one beside it, which
   shares .alert.warning but not this class) — no rule of its own since
   .alert.warning already styles it; kept as a selector so the class the
   markup carries is a real one, not a dangling name. */
.outcome-prompt {
}

/* ---------- landing hero ---------- */

.hero {
    text-align: center; padding: 3rem 1rem 2rem;
    background:
        radial-gradient(900px 400px at 85% -10%, rgba(13, 148, 254, 0.14), transparent 60%),
        radial-gradient(600px 300px at -10% 110%, rgba(5, 49, 121, 0.08), transparent 60%);
}
/* US-13.4.3 (STY-10/LAND-06): scales with the viewport instead of a single
   fixed size plus a phone-only override — clamp(min, preferred, max) means
   the rule is the same at every width: 1.8rem (~29px) on a 375px phone,
   2.9rem (~46px, unchanged from before) once 6vw passes it around 780px
   wide, so desktop rendering at 1280px is untouched. */
.hero h1 { font-size: clamp(1.8rem, 6vw, 2.9rem); max-width: 800px; margin: 0 auto .75rem; }
.hero h1 em { font-style: normal; color: var(--blue); }
.hero p.lead { max-width: 640px; margin: 0 auto 1.5rem; font-size: 1.1rem; color: var(--ink-2); }

.threecol { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.25rem; }

.iconcircle {
    width: 3rem; height: 3rem; border-radius: 50%;
    background: var(--blue-soft); color: var(--navy);
    display: flex; align-items: center; justify-content: center;
    font-size: 1.3rem; margin-bottom: .6rem; font-family: var(--head); font-weight: 700;
}

/* US-13.4.2 (STY-05): .9rem (14.4px) raised to .95rem — with the darker
   --muted token above, this is the app's whole "secondary text" floor. */
.muted { color: var(--muted); font-size: .95rem; }
/* US-13.4.2 (STY-05): the one line that decides what a customer does next —
   a match's agency/city/found-date line, a pipeline row's notice-number/
   agency line — reads as regular body text and colour, not secondary gray,
   even though it sits where a .muted meta line usually would. */
.meta-line { color: var(--ink-2); font-size: 1rem; }
.mono { font-family: Consolas, ui-monospace, monospace; font-size: .85rem; background: var(--blue-tint); padding: .15rem .4rem; border-radius: 6px; }
/* A short bold caption above a group of controls — e.g. "Try one:" (F08.6). */
.label { font-weight: 600; font-size: .85rem; color: var(--navy-ink); margin: 0 0 .35rem; }
.small { font-size: .82rem; }

/* ---------- spinner (a page working on something the customer is waiting on) ---------- */

.spinner {
    display: inline-block; width: 1rem; height: 1rem; margin-right: .5rem; vertical-align: -2px;
    border: 2px solid var(--blue-soft); border-top-color: var(--blue);
    border-radius: 50%; animation: spinner-turn .8s linear infinite;
}
@keyframes spinner-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .spinner { animation-duration: 2.4s; }
}

/* ---------- responsive ---------- */

@media (max-width: 720px) {
    .page { padding: 1rem .75rem; }
    .card { padding: 1rem; }
    h1 { font-size: 1.7rem; }
    /* US-13.4.2 (STY-05): a card heading (h2) never dominates a 375px first
       screen the way the un-shrunk 1.5rem default could. h1 above is
       unchanged by this story (already 1.7rem from the earlier LAND-06/nav
       work); h3 already sits at 1.15rem globally. */
    h2 { font-size: 1.15rem; }
    .brand .tag { display: none; }
    .brand .logo { height: 36px; }
    .topbar { padding: .45rem .75rem; }
    .hero { padding: 2rem 1rem 1.25rem; }
    /* US-10.4.4: the lost-connection bar's one action is a real touch
       target at phone width — the plain .btn.small it otherwise carries
       (padding .32rem .7rem, font-size .8rem) falls well short of 44px. */
    #blazor-error-ui .btn {
        display: inline-flex; align-items: center; justify-content: center;
        min-height: 44px; padding: .5rem 1rem; font-size: .95rem;
    }
}

#blazor-error-ui {
    background: var(--warn-bg); color: var(--navy-ink);
    bottom: 0; box-shadow: 0 -1px 2px rgba(0,0,0,.2);
    display: none; left: 0; padding: .6rem 1.25rem; position: fixed; width: 100%; z-index: 1000;
}
#blazor-error-ui .btn { margin-left: .6rem; vertical-align: middle; }

/* ----- AppInit: the branded wait/retry screen before a circuit is ready
   (US-01.7.1/2). Centred at every width; the button stays a full touch
   target even on a phone. ----- */
.appinit {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; gap: .75rem; min-height: 50vh; padding: 2rem 1rem;
}
.appinit .logo { height: 48px; width: auto; }
.appinit p { max-width: 26rem; margin: 0; }
.appinit .btn { min-height: 44px; }

.appinit .spinner {
    width: 2rem; height: 2rem; border-radius: 50%;
    border: 3px solid var(--blue-tint); border-top-color: var(--blue);
    animation: spin .9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .appinit .spinner { animation-duration: 2.4s; }
}

/* ----- C29: conversation with the agent ----- */
.chat { display: flex; flex-direction: column; gap: .75rem; max-height: 60vh; overflow-y: auto; }
.chat-turn { padding: .6rem .8rem; border-radius: 10px; max-width: 90%; }
.chat-user { align-self: flex-end; background: var(--blue-soft); }
.chat-agent { align-self: flex-start; background: #fff; border: 1px solid var(--line); }
/* US-13.4.2 (STY-05): .75rem (12px) raised to .85rem. */
.chat-who { font-size: .85rem; color: var(--muted); margin-bottom: .2rem; }
.chat-text p { margin: 0 0 .5rem; }
.chat-text p:last-child { margin-bottom: 0; }
/* F08.3/US-08.3.2: at least 0.85rem — never the old 0.75rem, too small to
   read comfortably as real citations rather than fine print. */
.chat-sources { font-size: .85rem; margin-top: .5rem; }
.chat-text ul, .chat-text ol { margin: 0 0 .5rem 1.2rem; padding: 0; }
.chat-text li { margin: .15rem 0; }
.chat-text table { border-collapse: collapse; margin: .3rem 0 .6rem; font-size: .9em; }
.chat-text th, .chat-text td { border: 1px solid var(--line); padding: .25rem .5rem; text-align: left; }
/* US-08.2.4: a real tap target for "Try again" despite .btn.small's smaller
   size — same pattern as .match-row-actions .btn above. */
.chat .alert.warning .btn { min-height: 44px; }

/* US-08.2.1/US-08.2.2: the progress line (stage + live elapsed counter + the
   expected-time hint) and the Cancel button beside it. Cancel gets a real
   44px tap target unconditionally, same as .chat .alert.warning .btn above. */
.chat-progress { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem; }
/* US-13.8.1 (undefined class): the stage sentence itself ("Looking… 4s"),
   bolded so it reads as the primary line beside .chat-progress-hint's
   smaller, muted expected-time note. */
.chat-progress-text { font-weight: 600; }
.chat-progress-hint { font-size: .8rem; }
.chat-cancel { margin-top: .4rem; min-height: 44px; }

/* F08.3/US-08.3.1: linked-opportunity cards under an answer — title link
   (or its pipeline stage badge, or Save) plus a deadline/days-left/source
   line. overflow-wrap keeps a long title from pushing the bubble sideways
   at 375px (.chat-turn's own max-width is only 90%, not full width). The
   title link and Save get a real 44px tap target unconditionally, same
   reasoning as .chat-cancel above: this sits inside a narrow chat bubble,
   not a full-width table row. */
.chat-opportunities { display: flex; flex-direction: column; gap: .6rem; margin-top: .6rem; }
.chat-opportunity { padding-top: .5rem; border-top: 1px solid var(--line); overflow-wrap: anywhere; }
.chat-opportunity-title { display: flex; flex-wrap: wrap; align-items: center; column-gap: .6rem; row-gap: .2rem; }
.chat-opportunity-link { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; }
.chat-opportunity-source { font-size: .85rem; }
.chat-opportunity-meta { margin: .1rem 0 .4rem; }
.chat-opportunity-save { min-height: 44px; }

/* F08.3/US-08.3.2: a section chip's inline expansion — the exact quoted
   text the answer's citation points at, collapsed again on a second click. */
.chat-section-quote {
    margin-top: .4rem; padding: .5rem .6rem; border-radius: 8px;
    background: var(--blue-tint); white-space: pre-wrap; font-size: .85rem;
}

/* F08.4/US-08.4.1: the "couldn't fully answer" note sits directly under that
   turn's own content — .alert's usual .75rem top margin assumes a page-level
   banner, not one already inside a compact chat bubble. Its "Ask a person"
   button picks up .chat .alert.warning .btn's 44px tap target above. */
.chat-turn .alert.warning.chat-unanswered { margin: .6rem 0 0; }
.chat-unanswered p { margin: 0 0 .4rem; }
.chat-unanswered .btn { margin-top: .2rem; }

/* US-08.4.3: the "not switched on yet" banner between the transcript and the
   question box — plain full-width text and a link, fits 375px unchanged. */
.chat-setup-banner { margin-bottom: 0; }

/* F08.5/US-08.5.1, US-08.5.2: the Fetch and HandOff go-ahead cards — a long
   file name wraps rather than pushing the card sideways at 375px, and each
   button gets a real 44px tap target, same pattern as .chat .alert.warning
   .btn above. */
.chat-consent p { overflow-wrap: anywhere; margin: 0 0 .1rem; }
.chat-consent .btn { min-height: 44px; }

/* F08.6/US-08.6.1: tappable example questions (and, without an opportunity in
   context, top-match chips) on an empty conversation — never the old italic,
   unclickable "Try:" line. Each button gets a real 44px tap target and, at
   phone width, the full row so a thumb can't miss it. */
.chat-examples { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .9rem; }
.chat-example { min-height: 44px; text-align: left; }
.chat-match-chips .chat-example { font-weight: 500; }
@media (max-width: 720px) {
    .chat-examples { flex-direction: column; }
    .chat-example { width: 100%; }
}

/* F08.6/US-08.6.3: the "recent 3" list under the examples on an otherwise
   empty New question tab. */
.chat-recent ul { margin: .3rem 0 .5rem 1.1rem; padding: 0; }
.chat-recent li { margin: .2rem 0; }

/* ASK-12/US-08.6.2: the caveats that used to open the page as a wall of
   muted text now live once, collapsed, between the one-line description and
   the tabs. */
.chat-caveats { margin: .3rem 0 1rem; }
.chat-caveats p { margin: .4rem 0 0; }

/* US-08.6.2, fixed after PR #620's adversarial review (see AgentChat.razor.js's
   wireChatSizing for the full story): position:sticky's "stick 64px from the
   viewport edge" is measured from the viewport, not from wherever .chat's own
   content ends, so on a short page — the empty conversation's three example
   buttons, barely taller than the header above them — the composer was
   already "stuck" on the very first frame, painting over those buttons and
   the AskTabs strip instead of after them, with a z-index that also stopped
   them being tapped. The composer now stays in NORMAL flow, directly under
   .chat; wireChatSizing sets .chat's own max-height (inline) to what is
   actually left above the composer and the bottom tab bar, so .chat scrolls
   its own turns internally and the composer is never covered. No mobile
   override is needed here any more — the unqualified .chat rule above
   (max-height: 60vh; overflow-y: auto) is only the pre-JS-interop fallback,
   and .chat-compose-card needs no positioning rule at all now that it is
   never sticky. */
/* US-13.8.1 (undefined class): kept as a real (empty) selector rather than
   only a mention in the comment above, now that the "every class used
   exists" test looks for a defined rule, not a code comment; .card.plain
   already carries every visual property this needs. */
.chat-compose-card {
}

/* F08.6/US-08.6.5: the read-only print/save transcript page — no header,
   footer, bottom tab bar or on-page buttons in the print preview. Scoped to
   just this page via the .print-transcript marker rather than a blanket
   print rule, since only this page promises "no navigation, footer or
   buttons" in its print preview. */
@media print {
    body:has(.print-transcript) .topbar,
    body:has(.print-transcript) .footerband,
    body:has(.print-transcript) .bottom-tabbar,
    body:has(.print-transcript) .no-print { display: none !important; }
}

/* F14.2/US-14.2.5: the free-review report prints without the site header,
   nav or navy footer — same body:has()/.no-print scoping the transcript
   print rule above already uses. A saved/printed copy needs the official
   link as text, not just a hyperlink, so .official-link's own href is
   appended after the link text (US-14.2.5's "each example's official link
   printed as its URL"). */
@media print {
    body:has(.report-page) .topbar,
    body:has(.report-page) .footerband,
    body:has(.report-page) .no-print { display: none !important; }
    .report-page .official-link[href]::after {
        content: " (" attr(href) ")";
        font-size: .85em;
        color: #555;
    }
}

/* US-13.7.4 (STY-16/PROS-11): the general print stylesheet — every OTHER
   page (Matches, Pipeline, Setup, Profile, Briefings, the admin console, …)
   gets the same "content, not chrome" treatment the two rules above already
   give the transcript and the free-review report, in one unscoped rule
   instead of a body:has() repeated per page. What's left on the printed
   page is the heading, the page's own text and its tables — no header nav,
   footer band, staff support banner, row/page action buttons, filter bar,
   lost-connection bar or reconnect dialog. .card drops its on-screen shadow
   for a plain, printable border and never splits across a page break. */
@media print {
    .topbar, .footerband, .support-banner, .actions, .actions-cell,
    .control-row, .bottom-tabbar, .skip-link,
    #blazor-error-ui, #components-reconnect-modal {
        display: none !important;
    }
    html, body { background: #fff; }
    .card {
        box-shadow: none;
        border-color: #999;
        break-inside: avoid;
    }
}

/* US-13.7.3 (STY-16): a member who told their OS or browser to reduce
   motion gets a still screen everywhere in the app, not only on the handful
   of components (the reconnect dialog's own scoped block above, the inline
   spinners) that already carried a bespoke prefers-reduced-motion override
   before this story. This catch-all forces every animation and transition
   in the product — the .btn colour transition, a card's hover, an
   accordion/collapsible opening, any future one nobody remembers to guard
   individually — to effectively no duration and a single frame; it takes
   priority (by design, via !important) over the narrower per-component
   duration choices already in this file, so "reduce motion" always means
   still, never merely slower. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* US-13.8.1 (undefined class): a comfortable reading width for the
   turn-by-turn transcript, echoing .legal's own max-width — the container
   itself carried no rule before, so .page's full 1080px went unbroken by
   any line length. */
.transcript { max-width: 70ch; }
.transcript-turn { padding: .5rem 0; border-bottom: 1px solid var(--line); }
.transcript-turn:last-child { border-bottom: none; }
.transcript-who { font-weight: 600; margin-bottom: .15rem; }
.transcript-text { white-space: pre-wrap; }
.transcript-sources { margin-top: .3rem; }

/* F09.5/US-09.5.1/US-09.5.3: Activity's "Details" expander — the raw Action,
   Item and Before/After values behind a plain sentence, for the person who
   wants them. A simple label:value list, not styled as a form. */
.audit-raw { margin: .4rem 0 0; font-size: .85rem; }
.audit-raw dt { font-weight: 600; color: var(--muted); float: left; clear: left; width: 9rem; }
.audit-raw dd { margin: 0 0 .2rem 9.5rem; word-break: break-word; }
.audit-burst-child td:first-child { padding-left: 1.5rem; }

/* Same disclosure pattern as .collapsible (always in the DOM so aria-controls
   always resolves), but for a <tbody> row-group rather than a <div> — plain
   display:block would break table layout, so the "open" state restores the
   table row-group display instead. */
.audit-burst-rows { display: none; }
.audit-burst-rows.open { display: table-row-group; }
@media (max-width: 480px) {
    .audit-raw dt { float: none; width: auto; }
    .audit-raw dd { margin-left: 0; margin-bottom: .5rem; }
}
