/* /Components/Pages/Admin/Clients.razor.rz.scp.css */
/*
    The tile strip at the top of the page.

    Labels are one or two lines depending on how long they are ("Past due" against "Trials
    ending ≤7d"), and with the number following the label in normal flow, that pushed the
    numbers to different heights — so the row read as ragged rather than as one set of
    figures to scan across.

    The cards are already equal height (.card.h-100 in a stretching row), so making each body
    a column and giving the number auto top margin drops every one of them to the same
    baseline. Doing it this way rather than fixing the label height means a third line, or a
    translation, still lines up instead of clipping.
*/
.stat-tiles .card-body[b-hp3wf9ukye] {
    display: flex;
    flex-direction: column;
}

.stat-tiles .card-body > :last-child[b-hp3wf9ukye] {
    margin-top: auto;
}
/* /Components/Pages/Admin/Customers.razor.rz.scp.css */
/* ============================================================================
   PHONE CARD LIST — contact-specific content (Docs/MOBILE_FIRST_INTERFACE.md §3.3)

   The chrome around these cards (.rec-card, .rec-cards-sort, .rec-cards-pager)
   is shared with All Work and lives in app.css. Only what makes a *contact*
   card a contact card is here.

   Authored mobile-first: these base rules ARE the phone layout, and the whole
   block is inert on desktop because .cust-cards carries d-md-none. app.css
   keeps its older desktop-first max-width convention (§3.2).
   ============================================================================ */

.cust-card-top[b-yakqc4dyqm] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
}

.cust-card-ident[b-yakqc4dyqm] {
    min-width: 0;
}

.cust-card-kicker[b-yakqc4dyqm] {
    display: block;
    font-size: 0.7rem;
    color: var(--muted-text);
}

/* DisplayName already folds the company in ("Powers, Mark — Acme Roofing"), so
   it gets two lines before truncating rather than the one a name alone needs. */
.cust-card-name[b-yakqc4dyqm] {
    font-size: 0.95rem;
    font-weight: 700;
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.cust-card-flags[b-yakqc4dyqm] {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex: 0 0 auto;
}

.cust-card-sub[b-yakqc4dyqm] {
    color: var(--warning-color);
    font-size: 0.95rem;
}

/* Our own crew, in the app's informational colour rather than the sub's warning
   amber — the two are different relationships, not two levels of the same one. */
.cust-card-emp[b-yakqc4dyqm] {
    color: var(--info-color, #4098d7);
    font-size: 0.95rem;
}

/* Every other contact type. Same size as the two above so the icons line up down a
   scrolling list, but muted: the card already says who this is, and the type is the
   supporting detail. Amber and blue stay meaningful only while nothing else claims a
   colour. */
.cust-card-type[b-yakqc4dyqm] {
    color: var(--muted-text);
    font-size: 0.95rem;
}

/* Only inactive is called out. Active is the overwhelming majority, and a chip
   on every card would be pure noise competing with the name above it. */
.cust-card-inactive[b-yakqc4dyqm] {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 0.1rem 0.4rem;
    border-radius: 99px;
    color: var(--muted-text);
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    white-space: nowrap;
}

.cust-card-loc[b-yakqc4dyqm] {
    font-size: 0.78rem;
    color: var(--muted-text);
    margin-top: 0.1rem;
}

.cust-card-actions[b-yakqc4dyqm] {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.45rem;
}

/* Dialling is the point of this card, so the number is a button-sized target
   rather than a line of text with a link somewhere in it. */
.cust-card-call[b-yakqc4dyqm] {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0 0.6rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--primary-color);
    background: var(--primary-soft);
    color: var(--primary-ink);
    font-weight: 700;
    font-size: 0.85rem;
    text-decoration: none;
}

.cust-card-num[b-yakqc4dyqm] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cust-card-kind[b-yakqc4dyqm] {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: 0.66rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    opacity: 0.75;
}

.cust-card-mail[b-yakqc4dyqm] {
    flex: 0 0 auto;
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--dark-text);
    text-decoration: none;
}

/* Contact type dropdown rows — the legend for the Type column's glyphs. Inline-flex
   rather than a MudBlazor utility class because the icon and the name have to sit on
   one baseline inside MudSelectItem's own padding, and a wrapping row would break the
   column of icons the eye reads down. */
.cust-type-option[b-yakqc4dyqm] {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}
/* /Components/Pages/Admin/Dispatch.razor.rz.scp.css */
/* Colors here come from the app's design tokens (app.css :root, re-pointed under
   [data-bs-theme="dark"]) rather than literal hex. The board used to hardcode a
   light palette — white cells, #f8f9fa headers, #6c757d text — which left it a
   glaring white slab in dark mode, and worse, produced text that vanished in two
   directions at once: cell/card text inherited the dark theme's near-white body
   color onto a still-white card (customer names at 1.3:1), while the rail's
   hardcoded dark #495057 sat on the genuinely dark rail (1.9:1). Tokens fix both
   ends, and light mode is unchanged to the eye. Semantic tints (today, over
   capacity, conflict, drop target) are mixed down from the soft/ink pairs so a
   whole-cell wash stays a hint rather than a shout. */

.dispatch-grid[b-j716romozg] {
    display: grid;
    width: 100%;
    overflow-x: auto;
    background: var(--border-strong); /* shows through as 1px gridlines */
    gap: 1px;
    border: 1px solid var(--border-strong);
    border-radius: .375rem;
}

/* ---- header ---- */
.dg-corner[b-j716romozg] {
    background: var(--light-bg);
    position: sticky;
    left: 0;
    z-index: 2;
}

.dg-dayhead[b-j716romozg] {
    background: var(--light-bg);
    text-align: center;
    padding: .4rem .25rem;
}

.dg-dayhead.is-today[b-j716romozg] {
    background: color-mix(in srgb, var(--primary-ink) 16%, var(--surface));
}

.dg-dow[b-j716romozg] {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--muted-text);
}

/* Muted grey lands under 4.5:1 once the today column is tinted (it did before
   this file used tokens, too). The accent both fixes the contrast and reads as
   part of the highlight. */
.dg-dayhead.is-today .dg-dow[b-j716romozg] {
    color: var(--primary-ink);
}

.dg-date[b-j716romozg] {
    font-weight: 600;
    font-size: .95rem;
    color: var(--dark-text);
}

/* ---- lane labels ---- */
.dg-lane[b-j716romozg] {
    background: var(--surface);
    padding: .5rem .6rem;
    display: flex;
    align-items: center;
    gap: .5rem;
    position: sticky;
    left: 0;
    z-index: 1;
}

.dg-lane.is-unassigned[b-j716romozg] {
    background: color-mix(in srgb, var(--warning-soft) 60%, var(--surface));
}

.dg-swatch[b-j716romozg] {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    border: 1px solid;
    flex: 0 0 auto;
}

.dg-lane-name[b-j716romozg] {
    font-weight: 600;
    font-size: .9rem;
    line-height: 1.1;
    color: var(--dark-text);
}

.dg-lane-sub[b-j716romozg] {
    font-size: .72rem;
    color: var(--muted-text);
}

/* ---- cells ---- */
.dg-cell[b-j716romozg] {
    background: var(--surface);
    min-height: 86px;
    padding: .3rem;
    display: flex;
    flex-direction: column;
    gap: .3rem;
    position: relative;
}

/* Capacity: a busy cell shows a count chip; over-capacity turns amber. */
.dg-cellbadge[b-j716romozg] {
    position: absolute;
    top: 3px;
    right: 3px;
    z-index: 1;
    font-size: .62rem;
    font-weight: 700;
    color: var(--dark-text);
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 0 .35rem;
    line-height: 1.4;
}

.dg-cellbadge.is-over[b-j716romozg] {
    background: var(--warning-soft);
    border-color: color-mix(in srgb, var(--warning-ink) 40%, transparent);
    color: var(--warning-ink);
}

.dg-cell.is-over[b-j716romozg] {
    background: color-mix(in srgb, var(--warning-soft) 45%, var(--surface));
}

.dg-cell.is-today.is-over[b-j716romozg] {
    background: color-mix(in srgb, var(--warning-soft) 62%, var(--surface));
}

/* Day-view double-booking conflict. */
.dg-cell.is-conflict[b-j716romozg] {
    background: color-mix(in srgb, var(--danger-soft) 55%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--danger-ink) 45%, transparent);
}

.dg-conflict-tag[b-j716romozg] {
    font-size: .6rem;
    font-weight: 700;
    color: var(--danger-ink);
    text-transform: uppercase;
    letter-spacing: .02em;
}

/* Per-lane load badge in lane labels/headers. */
.dg-laneload[b-j716romozg] {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: .68rem;
    font-weight: 700;
    color: var(--dark-text);
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 9px;
    padding: 0 .4rem;
    line-height: 1.5;
}

.dg-cell.is-today[b-j716romozg] {
    background: color-mix(in srgb, var(--primary-ink) 8%, var(--surface));
}

/* Drop target highlight while dragging a card over a cell. */
.dg-cell.is-drop-target[b-j716romozg] {
    background: color-mix(in srgb, var(--success-soft) 65%, var(--surface));
    box-shadow: inset 0 0 0 2px var(--success-ink);
}

/* ---- cards ---- */
.dg-card[b-j716romozg] {
    border: 1px solid var(--border-color);
    border-left: 5px solid #0d6efd;
    border-radius: .3rem;
    padding: .3rem .4rem;
    font-size: .78rem;
    cursor: pointer;
    background: var(--surface);
    color: var(--dark-text);
    box-shadow: var(--shadow-xs);
    transition: box-shadow .12s ease, transform .12s ease;
}

.dg-card:hover[b-j716romozg] {
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}

/* Bids read as lighter, outlined visits — distinct from solid job work. Mixed
   against --surface so the stripes stay a purple hint in either theme. */
.dg-card.is-bid[b-j716romozg] {
    background: repeating-linear-gradient(45deg,
        color-mix(in srgb, #6f42c1 6%, var(--surface)),
        color-mix(in srgb, #6f42c1 6%, var(--surface)) 6px,
        color-mix(in srgb, #6f42c1 15%, var(--surface)) 6px,
        color-mix(in srgb, #6f42c1 15%, var(--surface)) 12px);
    border-style: dashed;
}

.dg-card[draggable="true"][b-j716romozg] {
    cursor: grab;
}

.dg-card.is-dragging[b-j716romozg] {
    opacity: .4;
    cursor: grabbing;
}

.dg-card.is-complete[b-j716romozg] {
    opacity: .6;
}

.dg-card.is-complete .dg-cust[b-j716romozg] {
    text-decoration: line-through;
}

.dg-card-top[b-j716romozg] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: .25rem;
}

.dg-wo[b-j716romozg] {
    font-weight: 700;
    font-size: .72rem;
    color: var(--dark-text);
}

.dg-time[b-j716romozg] {
    font-size: .7rem;
    color: var(--muted-text);
    white-space: nowrap;
}

.dg-cust[b-j716romozg] {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--dark-text);
}

.dg-meta[b-j716romozg] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem;
    margin-top: .15rem;
    color: var(--muted-text);
    font-size: .7rem;
}

.dg-meta .badge[b-j716romozg] {
    font-size: .62rem;
    font-weight: 500;
}

.dg-men[b-j716romozg] {
    display: inline-flex;
    align-items: center;
    gap: .1rem;
}

/* ---- day time-grid ---- */
.dispatch-day[b-j716romozg] {
    display: grid;
    width: 100%;
    overflow-x: auto;
    background: var(--border-strong);
    gap: 1px;
    border: 1px solid var(--border-strong);
    border-radius: .375rem;
}

.dg-lanehead[b-j716romozg] {
    background: var(--light-bg);
    color: var(--dark-text);
    padding: .4rem .5rem;
    display: flex;
    align-items: center;
    gap: .4rem;
    font-weight: 600;
    font-size: .85rem;
    position: sticky;
    top: 0;
    z-index: 2;
}

.dg-lanehead.is-unassigned[b-j716romozg] {
    background: color-mix(in srgb, var(--warning-soft) 60%, var(--surface));
}

.dg-timelabel[b-j716romozg] {
    background: var(--light-bg);
    padding: .25rem .45rem;
    font-size: .72rem;
    color: var(--muted-text);
    text-align: right;
    white-space: nowrap;
    position: sticky;
    left: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

/* Day cells are shorter than week cells (one hour each, many rows). */
.dispatch-day .dg-cell[b-j716romozg] {
    min-height: 44px;
}

.dispatch-day .dg-anytime[b-j716romozg] {
    min-height: 36px;
    background: color-mix(in srgb, var(--light-bg) 45%, var(--surface));
}

/* ---- month heat-map ---- */
.dispatch-month[b-j716romozg] {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 1px;
    background: var(--border-strong);
    border: 1px solid var(--border-strong);
    border-radius: .375rem;
    overflow: hidden;
}

.dg-mhead[b-j716romozg] {
    background: var(--light-bg);
    text-align: center;
    padding: .35rem .25rem;
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--muted-text);
    font-weight: 600;
}

/* --dg-heat is the day's load as a percentage, set inline by HeatStyle. Mixing
   it toward --primary-ink keeps the heat direction right in both themes: the
   ink is a deep indigo on light (busier = deeper) and a pale periwinkle on dark
   (busier = brighter).

   --dg-heat-scale exists because those two directions are not symmetric. The
   10–55% range was picked against light, where the ink is DARKER than the
   surface: at full heat the cell reaches rgb(158,153,241) and the near-black
   body text still reads 6.05. On dark the ink is LIGHTER than the surface, so
   the same 55% lifts the cell to rgb(104,115,160) and the near-white body text
   falls to 3.77 — the busiest day of the month, the one most worth reading.

   Luminance climbs faster mixing up from #1e2330 than it falls mixing down from
   white, so the fix is to shorten the range on that side rather than to recolour
   the number: at 0.7 the top of the scale is rgb(82,91,127) and the day number
   reads 5.44, while the cell is still an obvious step above its quiet
   neighbours. */
.dg-mcell[b-j716romozg] {
    background: color-mix(in srgb,
        var(--primary-ink) calc(var(--dg-heat, 0%) * var(--dg-heat-scale, 1)),
        var(--surface));
    color: var(--dark-text);
    min-height: 92px;
    padding: .25rem;
    display: flex;
    flex-direction: column;
    gap: .2rem;
}

/* The one place this file names the theme rather than letting a token carry it,
   because the asymmetry is in the direction of the mix, which no token knows. */
[data-bs-theme="dark"] .dg-mcell[b-j716romozg] {
    --dg-heat-scale: .7;
}

.dg-mcell.is-out[b-j716romozg] {
    background: var(--light-bg);
    color: var(--muted-text);
}

.dg-mcell.is-today[b-j716romozg] {
    outline: 2px solid var(--primary-ink);
    outline-offset: -2px;
}

.dg-mcell.is-drop-target[b-j716romozg] {
    box-shadow: inset 0 0 0 2px var(--success-ink);
}

.dg-mday[b-j716romozg] {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.dg-mdaynum[b-j716romozg] {
    border: 0;
    background: transparent;
    font-size: .8rem;
    font-weight: 600;
    color: inherit;
    padding: 0 .2rem;
    border-radius: 3px;
    cursor: pointer;
}

.dg-mdaynum:hover[b-j716romozg] {
    background: color-mix(in srgb, var(--primary-ink) 18%, transparent);
}

.dg-mcell.is-out .dg-mdaynum[b-j716romozg] {
    color: var(--muted-text);
}

.dg-mcount[b-j716romozg] {
    font-size: .62rem;
    font-weight: 600;
    color: var(--dark-text);
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 0 .35rem;
}

.dg-mchips[b-j716romozg] {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    overflow: hidden;
}

.dg-chip[b-j716romozg] {
    display: flex;
    align-items: baseline;
    gap: .25rem;
    border-left: 4px solid #0d6efd;
    background: var(--surface);
    border-radius: .2rem;
    box-shadow: var(--shadow-xs);
    padding: .05rem .25rem;
    font-size: .68rem;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
}

.dg-chip.is-bid[b-j716romozg] {
    background: repeating-linear-gradient(45deg,
        color-mix(in srgb, #6f42c1 6%, var(--surface)),
        color-mix(in srgb, #6f42c1 6%, var(--surface)) 5px,
        color-mix(in srgb, #6f42c1 15%, var(--surface)) 5px,
        color-mix(in srgb, #6f42c1 15%, var(--surface)) 10px);
    border-left-style: dashed;
}

.dg-chip.is-dragging[b-j716romozg] {
    opacity: .4;
}

.dg-chip-wo[b-j716romozg] {
    font-weight: 700;
    color: var(--dark-text);
}

.dg-chip-cust[b-j716romozg] {
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--muted-text);
}

.dg-mmore[b-j716romozg] {
    border: 0;
    background: transparent;
    text-align: left;
    font-size: .65rem;
    color: var(--primary-ink);
    cursor: pointer;
    padding: 0 .2rem;
}

.dg-mmore:hover[b-j716romozg] {
    text-decoration: underline;
}

/* ---- unscheduled rail ---- */
.dispatch-layout[b-j716romozg] {
    /* let the board flex item shrink so its grid scrolls instead of overflowing */
    width: 100%;
}

.dispatch-rail[b-j716romozg] {
    flex: 0 0 230px;
    max-width: 230px;
    align-self: stretch;
}

.dispatch-rail.is-drop-target[b-j716romozg] {
    box-shadow: 0 0 0 2px #fd7e14;
}

.dispatch-rail-body[b-j716romozg] {
    max-height: 70vh;
    overflow-y: auto;
}

.dg-rail-title[b-j716romozg] {
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--muted-text);
    margin-bottom: .3rem;
}

.dg-rail-empty[b-j716romozg] {
    font-size: .75rem;
    color: var(--muted-text);
    padding: .1rem .2rem .3rem;
}

.dg-rail-card[b-j716romozg] {
    margin-bottom: .35rem;
}

/* ---- legend ---- */
.dg-legend[b-j716romozg] {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    margin-right: .3rem;
    vertical-align: -2px;
}

.dg-legend-job[b-j716romozg] {
    background: var(--surface);
    border: 1px solid var(--border-color);
    border-left: 5px solid #0d6efd;
}

.dg-legend-bid[b-j716romozg] {
    background: repeating-linear-gradient(45deg,
        color-mix(in srgb, #6f42c1 6%, var(--surface)),
        color-mix(in srgb, #6f42c1 6%, var(--surface)) 4px,
        color-mix(in srgb, #6f42c1 15%, var(--surface)) 4px,
        color-mix(in srgb, #6f42c1 15%, var(--surface)) 8px);
    border: 1px dashed #6f42c1;
}


/* A tab stop with no visible focus is a different failure from the one the
   tabindex fixed: these regions became keyboard-reachable (axe-core
   scrollable-region-focusable, WCAG 2.1.1) and would otherwise take focus
   invisibly. Inset, because an outline outside a scroll container is clipped by
   the container itself. */
.dispatch-grid:focus-visible[b-j716romozg],
.dispatch-day:focus-visible[b-j716romozg],
.dispatch-month:focus-visible[b-j716romozg],
.dispatch-rail-body:focus-visible[b-j716romozg] {
    outline: 2px solid var(--primary-color);
    outline-offset: -2px;
}
/* /Components/Pages/Admin/DispatchAgendaView.razor.rz.scp.css */
/* The phone agenda (Docs/DISPATCH_MOBILE.md §3.3).

   Everything here is unconditional rather than wrapped in a media query: the
   component is only rendered below Bootstrap's md by its host, so a second width
   test would be a second place to get the boundary wrong. The one exception is
   the touch-target block at the bottom, which documents the intent even though
   the sizes already hold at every width. */

.agenda[b-xziw46becs] {
    --ag-bar-h: 52px;
    padding-bottom: 1.5rem;
}

/* ---- Period bar ---------------------------------------------------------- */

/* top is the shell strip's height, NOT 0. At 0 this parks underneath
   .admin-strip, where it is not merely hidden but unclickable — the strip
   swallows the taps (mobile-first-plan2.md §9). */
.ag-periodbar[b-xziw46becs] {
    position: sticky;
    top: var(--shell-strip-h, 0px);
    z-index: 3;
    display: flex;
    align-items: center;
    gap: .25rem;
    min-height: var(--ag-bar-h);
    padding: .35rem .25rem;
    margin-bottom: .5rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border-color);
}

.ag-nav[b-xziw46becs],
.ag-today[b-xziw46becs] {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-color);
    border-radius: .5rem;
    background: var(--surface);
    color: var(--dark-text);
    font-size: .9rem;
}

.ag-today[b-xziw46becs] {
    padding: 0 .75rem;
    font-weight: 600;
}

.ag-nav:disabled[b-xziw46becs],
.ag-today:disabled[b-xziw46becs] {
    opacity: .5;
}

.ag-period[b-xziw46becs] {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-weight: 600;
    font-size: .95rem;
    color: var(--dark-text);
}

/* Focusable and activatable, but not laid out: clipped to a pixel rather than
   display:none, because a hidden input cannot be reached by its label and its
   picker cannot be opened. */
.ag-jump[b-xziw46becs] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    opacity: 0;
    inset-inline-start: 50%;
    bottom: 0;
}

/* ---- Capacity strip ------------------------------------------------------ */

/* 1px of gap, not 2. Seven columns inside the container's padding leave 318.6px
   at 375, which a 2px gap divides into 43.8px columns — 0.2px under the touch
   minimum this codebase holds everywhere else. 1px gives 44.7. */
.ag-strip[b-xziw46becs] {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    margin-bottom: .75rem;
}

/* The heat tint is mixed from the theme's primary rather than a hardcoded blue,
   so it stays a tint in both themes. --ag-heat is 0-1 against the busiest day in
   the window; 40% keeps the fullest day readable rather than solid. */
.ag-strip-day[b-xziw46becs] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    min-height: 48px;
    padding: .25rem 0;
    border: 1px solid var(--border-color);
    border-radius: .4rem;
    background: var(--surface);
    background: color-mix(in srgb, var(--primary-color) calc(var(--ag-heat, 0) * 40%), var(--surface));
    color: var(--dark-text);
}

.ag-strip-day.is-today[b-xziw46becs] {
    border-color: var(--primary-color);
    border-width: 2px;
}

.ag-strip-day.is-over[b-xziw46becs] {
    border-color: var(--danger-ink);
}

/* --dark-text, not --muted-text. The cell's background is a variable tint, so
   any label colour has to clear 4.5 against the *heaviest* tint, not against
   --surface: on the busiest day of the window --muted-text measured 2.70. Size,
   case and letter-spacing carry the hierarchy instead of colour. */
.ag-strip-dow[b-xziw46becs] {
    font-size: .65rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--dark-text);
}

.ag-strip-n[b-xziw46becs] {
    font-size: .95rem;
    font-weight: 700;
    line-height: 1;
}

.ag-strip-day.is-over .ag-strip-n[b-xziw46becs] {
    color: var(--danger-ink);
}

/* ---- Filters ------------------------------------------------------------- */

/* Shared by the filter row and the backlog row — two disclosures that should not
   look like two different controls. */
.ag-disclosure[b-xziw46becs] {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    min-height: 44px;
    padding: 0 .75rem;
    margin-bottom: .5rem;
    border: 1px solid var(--border-color);
    border-radius: .5rem;
    background: var(--surface);
    color: var(--dark-text);
    font-size: .85rem;
}

.ag-filters-summary[b-xziw46becs] {
    color: var(--muted-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ag-panel[b-xziw46becs] {
    padding: .5rem .75rem .75rem;
    margin-bottom: .75rem;
    border: 1px solid var(--border-color);
    border-radius: .5rem;
    background: var(--surface);
}

.ag-filter-row[b-xziw46becs] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem;
    margin-top: .5rem;
}

.ag-filter-row:first-child[b-xziw46becs] {
    margin-top: 0;
}

.ag-filter-label[b-xziw46becs] {
    flex: 0 0 3.2rem;
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--muted-text);
}

/* Plain pills rather than MudToggleGroup. The Mud groups fail contrast three
   separate ways in dark mode (§8: 1.06 inactive on Color.Dark, 2.56 on the
   secondary fill, 3.30 on the primary fill), and every one of those is inherited
   from the palette. Not rendering them is a smaller fix than overriding them. */
.ag-pill[b-xziw46becs] {
    min-height: 44px;
    padding: 0 .85rem;
    border: 1px solid var(--border-color);
    border-radius: 999px;
    background: var(--surface);
    color: var(--dark-text);
    font-size: .8rem;
    font-weight: 500;
}

.ag-pill.active[b-xziw46becs] {
    background: var(--primary-soft);
    border-color: var(--primary-ink);
    color: var(--primary-ink);
    font-weight: 600;
}

.ag-pill:disabled[b-xziw46becs] {
    opacity: .5;
}

.ag-area[b-xziw46becs] {
    flex: 1 1 8rem;
    min-height: 44px;
    font-size: .85rem;
}

/* ---- Days ---------------------------------------------------------------- */

/* The one offset scrollIntoView actually honours. Without it the strip's tap
   lands the day header underneath the sticky period bar. */
.ag-day[b-xziw46becs] {
    scroll-margin-top: calc(var(--shell-strip-h, 0px) + var(--ag-bar-h) + .5rem);
}

.ag-day-head[b-xziw46becs] {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    margin: 1rem 0 .4rem;
    font-size: .95rem;
    font-weight: 700;
    color: var(--dark-text);
}

.ag-day-head.is-today[b-xziw46becs] {
    color: var(--primary-ink);
}

.ag-day-date[b-xziw46becs] {
    font-size: .8rem;
    font-weight: 500;
    color: var(--muted-text);
}

.ag-count[b-xziw46becs] {
    margin-left: auto;
    padding: .1rem .45rem;
    border-radius: 999px;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    font-size: .7rem;
    font-weight: 600;
    color: var(--muted-text);
}

/* The backlog row's count sits beside its label, not pushed to the far edge —
   the chevron takes the ms-auto there. */
.ag-disclosure .ag-count[b-xziw46becs] {
    margin-left: 0;
}

/* Stale for the moment a re-query takes. Dimming beats replacing: the period bar
   and disclosures above stay put and the page does not jump under a thumb. */
.ag-days.is-busy[b-xziw46becs] {
    opacity: .55;
    pointer-events: none;
}

.ag-day-empty[b-xziw46becs] {
    margin: 0 0 .25rem;
    padding: .5rem .75rem;
    font-size: .8rem;
    color: var(--muted-text);
    border: 1px dashed var(--border-color);
    border-radius: .5rem;
}

/* ---- Cards --------------------------------------------------------------- */

/* A <button>, not a <div> with @onclick: it is the whole tap target, and it is
   the only way the card is reachable by keyboard. */
.ag-card[b-xziw46becs] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: .15rem;
    width: 100%;
    min-height: 44px;
    padding: .6rem .75rem;
    margin-bottom: .4rem;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--border-color);
    border-left: 4px solid var(--border-strong);
    border-radius: .5rem;
    box-shadow: var(--shadow-xs);
    color: var(--dark-text);
}

.ag-card.is-bid[b-xziw46becs] {
    background: repeating-linear-gradient(
        135deg,
        var(--surface),
        var(--surface) 8px,
        var(--light-bg) 8px,
        var(--light-bg) 16px);
}

.ag-card.is-complete[b-xziw46becs] {
    opacity: .65;
}

.ag-card-top[b-xziw46becs] {
    display: flex;
    align-items: baseline;
    gap: .5rem;
}

.ag-time[b-xziw46becs] {
    font-size: .8rem;
    font-weight: 700;
    color: var(--primary-ink);
}

.ag-wo[b-xziw46becs] {
    margin-left: auto;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .02em;
    color: var(--muted-text);
}

.ag-cust[b-xziw46becs] {
    font-size: .95rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ag-lane[b-xziw46becs] {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: .8rem;
    color: var(--muted-text);
}

.ag-lane.is-unassigned[b-xziw46becs] {
    color: var(--warning-ink);
    font-weight: 600;
}

.ag-meta[b-xziw46becs] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    font-size: .75rem;
    color: var(--muted-text);
}

.ag-meta i[b-xziw46becs] {
    margin-right: .2rem;
}

.ag-status[b-xziw46becs] {
    padding: .1rem .45rem;
    border: 1px solid var(--border-color);
    border-radius: .3rem;
    background: var(--light-bg);
}

/* ---- Action sheet -------------------------------------------------------- */

/* Bottom-anchored rather than centred: on a phone the reachable half of the
   screen is the bottom one, and this sheet exists to be tapped. */
.ag-scrim[b-xziw46becs] {
    position: fixed;
    inset: 0;
    z-index: 1080;
    background: rgba(0, 0, 0, .45);
}

.ag-sheet[b-xziw46becs] {
    position: fixed;
    z-index: 1081;
    inset-inline: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    max-height: 85vh;
    overflow-y: auto;
    /* No tab-bar clearance needed: .admin-tabbar measures z-index 1035, so both
       the scrim and the sheet paint over it. The inset is for the home indicator
       on a notched phone, not for the bar. */
    padding: .5rem 1rem calc(1.25rem + env(safe-area-inset-bottom, 0px));
    background: var(--surface);
    border-top: 1px solid var(--border-color);
    border-radius: 1rem 1rem 0 0;
    box-shadow: var(--shadow-lg);
}

.ag-sheet-grip[b-xziw46becs] {
    width: 2.5rem;
    height: 4px;
    margin: .25rem auto .5rem;
    border-radius: 999px;
    background: var(--border-strong);
}

.ag-sheet-head[b-xziw46becs] {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--border-color);
}

.ag-sheet-title[b-xziw46becs] {
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .02em;
    color: var(--muted-text);
}

.ag-sheet-sub[b-xziw46becs] {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--dark-text);
}

.ag-sheet-btn[b-xziw46becs] {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    min-height: 48px;
    padding: 0 .9rem;
    border: 1px solid var(--border-color);
    border-radius: .6rem;
    background: var(--surface);
    color: var(--dark-text);
    font-size: .95rem;
    font-weight: 500;
    text-align: left;
}

/* The two phone-native actions. Tinted so they read as the primary things to do
   from a phone without competing with the Confirm button, which is the only
   control that commits a change. */
.ag-sheet-btn.is-action[b-xziw46becs] {
    background: var(--primary-soft);
    border-color: var(--primary-soft);
    color: var(--primary-ink);
    font-weight: 600;
    text-decoration: none;
}

/* --primary-ink, not --muted-text. The hint sits on the --primary-soft tint, and
   muted-on-soft measured 4.26 — the same mistake as the capacity strip's
   day-of-week label (§9), in a second place. Weight and size carry the hierarchy;
   the soft/ink pair is the only colour combination the tint guarantees. */
.ag-sheet-hint[b-xziw46becs] {
    margin-left: auto;
    font-size: .75rem;
    font-weight: 500;
    color: var(--primary-ink);
}

.ag-sheet-btn.is-primary[b-xziw46becs] {
    background: var(--primary-soft);
    border-color: var(--primary-ink);
    color: var(--primary-ink);
    font-weight: 600;
    justify-content: center;
}

.ag-sheet-btn.is-danger[b-xziw46becs] {
    color: var(--danger-ink);
    border-color: var(--danger-ink);
}

.ag-sheet-btn.is-plain[b-xziw46becs] {
    justify-content: center;
    border-color: transparent;
    color: var(--muted-text);
}

.ag-sheet-actions[b-xziw46becs] {
    display: flex;
    gap: .5rem;
}

.ag-sheet-actions .ag-sheet-btn[b-xziw46becs] {
    flex: 1 1 0;
}

.ag-field[b-xziw46becs] {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    border: 0;
    padding: 0;
    margin: 0;
}

.ag-field-label[b-xziw46becs] {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--muted-text);
}

.ag-field .form-control[b-xziw46becs],
.ag-field .form-select[b-xziw46becs] {
    min-height: 48px;
}

.ag-lane-list[b-xziw46becs] {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

.ag-lane-opt[b-xziw46becs] {
    display: flex;
    align-items: center;
    gap: .6rem;
    min-height: 48px;
    padding: 0 .75rem;
    border: 1px solid var(--border-color);
    border-radius: .5rem;
    background: var(--surface);
    color: var(--dark-text);
    font-size: .9rem;
    text-align: left;
}

.ag-lane-opt.active[b-xziw46becs] {
    background: var(--primary-soft);
    border-color: var(--primary-ink);
    color: var(--primary-ink);
    font-weight: 600;
}

.ag-lane-swatch[b-xziw46becs] {
    flex: 0 0 auto;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
}

/* Documents the intent even where the sizes above already satisfy it: every
   control on this view is a touch target first. */
@media (max-width: 767.98px) {
    .agenda button[b-xziw46becs],
    .agenda .form-select[b-xziw46becs],
    .agenda .form-control[b-xziw46becs] {
        min-height: 44px;
    }
}
/* /Components/Pages/Admin/GettingStarted.razor.rz.scp.css */
/* One readable column. The checklist is prose with buttons on it, so it takes a
   measure rather than the full page width — at 1440px a nine-item list stretched
   edge to edge and the CTA ended up a foot from the step it belonged to. */
.gs-wrap[b-6s7n8ae658] {
    max-width: 44rem;
}
/* /Components/Pages/Admin/Glossary.razor.rz.scp.css */
.glossary[b-y8li5hh45t] {
    max-width: 760px;
    display: flex;
    flex-direction: column;
    gap: 1.75rem;
}

.glossary-group-title[b-y8li5hh45t] {
    font-size: 0.72rem;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted-text);
    margin: 0 0 0.6rem;
    padding-bottom: 0.4rem;
    border-bottom: 1px solid var(--border-color);
}

.glossary-term[b-y8li5hh45t] {
    /* Clear the sticky top strip when jumped to via an #anchor. */
    scroll-margin-top: 64px;
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--border-color);
}
.glossary-group .glossary-term:last-child[b-y8li5hh45t] { border-bottom: 0; }

.glossary-term-name[b-y8li5hh45t] {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 0 0 0.25rem;
    color: var(--dark-text);
}
.glossary-term-short[b-y8li5hh45t] {
    font-size: 0.92rem;
    font-weight: 500;
    margin: 0 0 0.4rem;
    color: var(--dark-text);
}
.glossary-term-long[b-y8li5hh45t] {
    font-size: 0.88rem;
    line-height: 1.6;
    margin: 0 0 0.5rem;
    color: var(--muted-text);
}

.glossary-see[b-y8li5hh45t] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.82rem;
}
.glossary-see-label[b-y8li5hh45t] { color: var(--muted-text); }
.glossary-see-link[b-y8li5hh45t] {
    display: inline-flex;
    align-items: center;
    padding: 0.1rem 0.55rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 999px;
    color: var(--primary-color);
    text-decoration: none;
}
.glossary-see-link:hover[b-y8li5hh45t] { border-color: var(--primary-color); }

/* Briefly highlight the term you jumped to. */
.glossary-term:target .glossary-term-name[b-y8li5hh45t] { color: var(--primary-color); }
/* /Components/Pages/Admin/JobHub.razor.rz.scp.css */
.hub-header h4 a:hover[b-s76vjpjp95] { text-decoration: underline !important; }

/* ── Touch targets, under md ────────────────────────────────────────────────
   Every control this page owns, raised to 44px on a phone. Scoped to .hub-page
   rather than raised on .btn-sm across admin — round 1 §5 declined the blanket
   version and Phase 4 declined it again for the Pipeline filter toggle. What is
   new here is only that this page opts in.

   Measured before: back chevron 23.5x34.3, Duplicate and Open full editor 32.4,
   both tab strips 40.4, the map and pencil deep-links 14.2x17. The map link was
   the smallest tap target found in five phases. */
@media (max-width: 767.98px) {
    .hub-page .btn[b-s76vjpjp95],
    .hub-page .nav-tabs .nav-link[b-s76vjpjp95] {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* The back chevron is a .btn-link with p-0, so min-height alone leaves a
       23px-wide target. Square it off. */
    .hub-page .hub-back[b-s76vjpjp95] {
        min-width: 44px;
        justify-content: flex-start;
    }

    /* Icon-only deep links (open map, edit in full editor). They sit inside text
       rows, so they take their target from padding and a negative margin that
       keeps the row's height and alignment unchanged. */
    .hub-page .hub-icon-link[b-s76vjpjp95] {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 44px;
        min-height: 44px;
        margin-top: -0.7rem;
        margin-bottom: -0.7rem;
        vertical-align: middle;
    }

    /* View all / Show less is a .btn-link inside the gallery header; the shared
       rule above would centre it and pull the header apart. */
    .hub-page .hub-showall[b-s76vjpjp95] { justify-content: flex-end; }

    /* Tap to call, tap to text, tap to email. These measured 15-17px tall and
       they are the whole reason round 1 called this "the page that already knows
       what a field user needs" — the phone number is the point of opening it in
       a driveway. They sit in their own rows rather than mid-sentence, so they
       can take a real target without breaking a line box.

       The header's duplicate tel:/address links are deliberately NOT raised:
       those are inline inside a flowing muted line, where a 44px box would
       break the text the way the signing page's ESIGN toggle did before it was
       given its own line. The fact rows below are the tappable copies. */
    .hub-page .hub-fact-row a:not(.hub-icon-link)[b-s76vjpjp95],
    .hub-page .hub-fact-row + a[b-s76vjpjp95],
    .hub-page .hub-edit-customer[b-s76vjpjp95] {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-height: 44px;
        /* Text is the shortest of these at 41.8px wide — 2.2px short of a square
           target for no reason other than the length of the word. */
        min-width: 44px;
    }

    /* Attachment links inside a message bubble — the way a photo someone sent
       gets opened. */
    .hub-page .hub-message a[b-s76vjpjp95] {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}

/* Page-type label above the customer name. Small and muted — it should answer
   "what page is this?" once, without competing with the name below it. */
.hub-kicker[b-s76vjpjp95] {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1;
    margin-bottom: 0.25rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ── Customer fact rows ─────────────────────────────────────────────────── */

.hub-fact-row[b-s76vjpjp95] {
    display: flex;
    gap: 0.75rem;
    padding: 0.45rem 0;
    border-bottom: 1px solid var(--bs-border-color-translucent, rgba(0,0,0,.08));
    align-items: baseline;
}

.hub-fact-row > i[b-s76vjpjp95] {
    color: var(--bs-secondary-color, #6c757d);
    width: 1.2rem;
    text-align: center;
    flex-shrink: 0;
}

/* ── Messages ───────────────────────────────────────────────────────────── */

.hub-messages[b-s76vjpjp95] {
    max-height: 380px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-right: 0.25rem;
}

/* Pattern F — the nested scroller, under md (mobile-first-plan2.md §3.6, §13).
   Measured here: a 380px box holding 1,835px of thread, so the phone showed 21%
   of a conversation through 47% of its viewport, and the flick that should move
   the page was swallowed by the inner list.

   This rule lives HERE and not beside .thread-messages in app.css, where §3.6
   drafted it as one shared block, because .hub-messages is scoped CSS: it
   compiles to `.hub-messages[b-s76vjpjp95]` at (0,2,0), and a bare .hub-messages
   in app.css is (0,1,0). Load order cannot save a weaker selector, so the shared
   version would have parsed, matched, and silently lost. */
@media (max-width: 767.98px) {
    .hub-messages[b-s76vjpjp95] {
        max-height: none;
        overflow-y: visible;
    }
}

/* Where "the newest message" is allowed to land. OnAfterRenderAsync scrolls the
   last bubble to the bottom of the viewport, and the bottom of the viewport is
   where the shell's fixed tab bar lives — measured 69px of the last message
   parked behind it. scroll-margin-bottom is the one thing scrollIntoView
   honours, so the offset belongs here rather than in the call.

   The token reads 56px but the bar renders 69 (its own padding), so most of the
   1.5rem is not slack: 13px of it closes the gap between the declared height and
   the painted one, and the remaining 11px is the air that makes the bubble read
   as sitting above the bar rather than jammed against it. */
.hub-message[id][b-s76vjpjp95] {
    scroll-margin-bottom: calc(var(--shell-tabbar-h, 0px) + 1.5rem);
}

.hub-message[b-s76vjpjp95] {
    max-width: 85%;
    padding: 0.45rem 0.7rem;
    border-radius: 0.6rem;
    font-size: 0.875rem;
    white-space: pre-wrap;
}

.hub-message.from-admin[b-s76vjpjp95] {
    align-self: flex-end;
    background: color-mix(in srgb, var(--bs-primary, #0d6efd) 12%, var(--bs-body-bg, #fff));
    border: 1px solid color-mix(in srgb, var(--bs-primary, #0d6efd) 25%, transparent);
}

.hub-message.from-customer[b-s76vjpjp95] {
    align-self: flex-start;
    background: var(--bs-tertiary-bg, #f8f9fa);
    border: 1px solid var(--bs-border-color, #dee2e6);
}

.hub-message-meta[b-s76vjpjp95] {
    font-size: 0.72rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-bottom: 0.15rem;
}

/* ── Tasks ──────────────────────────────────────────────────────────────── */

.hub-task-row:last-of-type[b-s76vjpjp95] { border-bottom: none !important; }

/* ── Photos & Files gallery ─────────────────────────────────────────────── */

.hub-file-grid[b-s76vjpjp95] {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.5rem;
}

@media (max-width: 1200px) { .hub-file-grid[b-s76vjpjp95] { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 480px)  { .hub-file-grid[b-s76vjpjp95] { grid-template-columns: repeat(2, 1fr); } }

.hub-file-tile[b-s76vjpjp95] {
    position: relative;
    aspect-ratio: 1 / 1;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    overflow: hidden;
    padding: 0;
    background: var(--bs-tertiary-bg, #f1f3f5);
    cursor: pointer;
}

.hub-file-tile img[b-s76vjpjp95] {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.hub-file-glyph[b-s76vjpjp95] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    font-size: 2rem;
    color: var(--bs-secondary-color, #6c757d);
}

.hub-file-chip[b-s76vjpjp95] {
    position: absolute;
    top: 0.3rem;
    left: 0.3rem;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    padding: 0.1rem 0.35rem;
    border-radius: 0.25rem;
    color: #fff;
    text-transform: uppercase;
}

.hub-file-chip.static[b-s76vjpjp95] { position: static; }

/* Kind badges. White on a solid fill, 9.9px — so these are held to the 4.5 floor
   for small text, and two of the four missed it: receipt at 3.19 and contract at
   3.77. Both replaced with the 700 step of the same hue, which keeps the badges
   reading as the same four colours while carrying white properly:

       field    #2563eb  5.12  (unchanged)
       receipt  #d97706  3.19 -> #b45309  5.05
       message  #7c3aed  5.70  (unchanged)
       contract #059669  3.77 -> #047857  5.55

   Fixed values rather than tokens, for the reason app.css records at length: a
   solid fill under white text must not flip with the theme. */
.chip-field[b-s76vjpjp95]    { background: #2563eb; }
.chip-receipt[b-s76vjpjp95]  { background: #b45309; }
.chip-message[b-s76vjpjp95]  { background: #7c3aed; }
.chip-contract[b-s76vjpjp95] { background: #047857; }

.hub-file-caption[b-s76vjpjp95] {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 0.6rem 0.4rem 0.3rem;
    font-size: 0.7rem;
    color: #fff;
    text-align: left;
    background: linear-gradient(transparent, rgba(0,0,0,0.72));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0;
    transition: opacity 0.12s ease;
}

.hub-file-tile:hover .hub-file-caption[b-s76vjpjp95],
.hub-file-tile:focus-visible .hub-file-caption[b-s76vjpjp95] { opacity: 1; }

/* A phone has no hover, so under md every caption in this gallery was
   unreachable — present in the DOM, painted, and at opacity 0 forever. Shown
   unconditionally instead. The gradient behind it is what makes this safe to
   leave on: it is already sized to carry white text over an arbitrary photo. */
@media (max-width: 767.98px) {
    .hub-file-caption[b-s76vjpjp95] { opacity: 1; }
}

/* ── Kind filter ────────────────────────────────────────────────────────────
   Rendered only for a gallery long enough to be work to scan and mixed enough
   to separate (see ShowKindFilter). Scrolls sideways rather than wrapping: a
   second row of chips would push the first tiles off a phone screen, which is
   the height this filter exists to save. */

.hub-kind-filter[b-s76vjpjp95] {
    display: flex;
    gap: 0.35rem;
    margin-bottom: 0.6rem;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.hub-kind-filter[b-s76vjpjp95]::-webkit-scrollbar { display: none; }

.hub-kind-btn[b-s76vjpjp95] {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    min-height: 36px;
    padding: 0.25rem 0.7rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 999px;
    background: var(--surface);
    color: var(--dark-text);
    font-size: 0.8rem;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
}

.hub-kind-btn.active[b-s76vjpjp95] {
    /* --primary-color is a solid brand fill and does not flip, so white on it is
       the same reading in both themes — the trap app.css records at length is
       using an --x-ink token as a background instead. */
    background: var(--primary-color);
    border-color: var(--primary-color);
    color: #fff;
}

.hub-kind-n[b-s76vjpjp95] { opacity: 0.7; font-variant-numeric: tabular-nums; }

/* View all / Show less. A .btn-link takes Bootstrap's link blue, which measured
   3.31:1 on the dark card; --primary-ink is the token that flips, and it is what
   every other classless link in the app was moved to in Phase 3. */
.hub-showall[b-s76vjpjp95] { color: var(--primary-ink); }

@media (max-width: 767.98px) {
    .hub-kind-btn[b-s76vjpjp95] { min-height: 44px; }
}

/* ── Lightbox ───────────────────────────────────────────────────────────── */

.hub-lightbox[b-s76vjpjp95] {
    position: fixed;
    inset: 0;
    z-index: 1400;
    background: rgba(0,0,0,0.82);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

.hub-lightbox-inner[b-s76vjpjp95] {
    width: min(1100px, 96vw);
    height: min(88vh, 900px);
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.hub-lightbox-bar[b-s76vjpjp95] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    color: #fff;
    font-size: 0.9rem;
}

.hub-lightbox-stage[b-s76vjpjp95] {
    position: relative;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
}

.hub-lightbox-stage img[b-s76vjpjp95] {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: 0.4rem;
}

.hub-lightbox-stage iframe[b-s76vjpjp95] {
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 0.4rem;
    background: #fff;
}

.hub-lightbox-nav[b-s76vjpjp95] {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    /* 44px, not 2.6rem (= 41.6px). Unconditional rather than under md: this is
       the control you tap repeatedly to walk a job's photos, and 44 is not a
       size a mouse minds. */
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 0;
    background: rgba(255,255,255,0.15);
    color: #fff;
    font-size: 1.2rem;
}
.hub-lightbox-nav:hover[b-s76vjpjp95] { background: rgba(255,255,255,0.3); }
.hub-lightbox-nav.prev[b-s76vjpjp95] { left: 0.4rem; }
.hub-lightbox-nav.next[b-s76vjpjp95] { right: 0.4rem; }

@media (max-width: 767.98px) {
    /* Close and Open/download. Close especially: it is the way out of a
       full-screen overlay, and it was the smallest control in it. */
    .hub-lightbox-bar .btn[b-s76vjpjp95] {
        min-height: 44px;
        min-width: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/Admin/JobStageTracker.razor.rz.scp.css */
/* Horizontal scroll wrapper: under ~900px the six columns keep a usable
   minimum width and the strip scrolls sideways instead of crushing. */
.tracker-scroll[b-b4e5hnp4oe] {
    overflow-x: auto;
}

.tracker-grid[b-b4e5hnp4oe] {
    display: grid;
    grid-template-columns: repeat(6, minmax(140px, 1fr));
    gap: 0.4rem 0.5rem;
    min-width: 880px;
}

/* ── Group rail ─────────────────────────────────────────────────────────── */

.tracker-rail-chip[b-b4e5hnp4oe] {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.6rem;
    border-radius: 0.35rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    border: 1px solid var(--accent);
    /* The border keeps the brand accent; the label needs the readable pair, for
       the reason app.css's PIPELINE GROUP ACCENTS block sets out. The .done and
       .current variants below fill with --accent under #fff and are unaffected —
       a fill is exactly what the accent is for. */
    color: var(--accent-ink, var(--accent));
    background: transparent;
}

.tracker-rail-chip.done[b-b4e5hnp4oe],
.tracker-rail-chip.current[b-b4e5hnp4oe] {
    background: var(--accent);
    color: #fff;
}

.tracker-rail-chip.current[b-b4e5hnp4oe] {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent);
}

.tracker-rail-chip.future[b-b4e5hnp4oe] { opacity: 0.65; }

.tracker-rail-chip.skipped[b-b4e5hnp4oe],
.tracker-rail-chip.dead[b-b4e5hnp4oe] {
    border-style: dashed;
    border-color: var(--bs-secondary-color, #6c757d);
    color: var(--bs-secondary-color, #6c757d);
    opacity: 0.7;
}

.tracker-glyph[b-b4e5hnp4oe] { font-size: 0.8rem; line-height: 1; }

/* ── Stage cards ────────────────────────────────────────────────────────── */

.tracker-card[b-b4e5hnp4oe] {
    padding: 0.5rem 0.65rem;
    border-radius: 0.4rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-body-bg, #fff);
    min-height: 3.4rem;
}

.tracker-card.done[b-b4e5hnp4oe] {
    border-left: 3px solid var(--accent);
}

.tracker-card.done .tracker-card-label i[b-b4e5hnp4oe] { color: var(--accent-ink, var(--accent)); }

.tracker-card.current[b-b4e5hnp4oe] {
    border: 2px solid var(--accent);
    box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 25%, transparent);
    font-weight: 600;
}

/* Follow-up Due: warn on the current card without losing its group accent. */
.tracker-card.current.nudge[b-b4e5hnp4oe] {
    border-color: var(--bs-warning, #ffc107);
    box-shadow: 0 2px 10px color-mix(in srgb, var(--bs-warning, #ffc107) 35%, transparent);
}

/* Next-step action button on the current card. */
.tracker-action-btn[b-b4e5hnp4oe] {
    display: inline-flex;
    align-items: center;
    width: 100%;
    justify-content: center;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: #fff;
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.28rem 0.5rem;
    border-radius: 0.35rem;
    line-height: 1.1;
}

.tracker-action-btn:hover[b-b4e5hnp4oe] {
    filter: brightness(1.08);
}

/* 38.4px measured on a phone, and this is the tracker's whole point: the one
   next step for this job ("Record payment", "Send estimate"). The component is
   used on exactly one page — the Job Hub — so this is that page's floor
   reaching a control that lives in a shared file, not a change to admin at
   large. */
@media (max-width: 767.98px) {
    .tracker-action-btn[b-b4e5hnp4oe] { min-height: 44px; }
}

.tracker-card.current.nudge .tracker-action-btn[b-b4e5hnp4oe] {
    border-color: var(--bs-warning, #ffc107);
    background: var(--bs-warning, #ffc107);
    color: #1a1a1a;
}

.tracker-card.future[b-b4e5hnp4oe],
.tracker-card.skipped[b-b4e5hnp4oe],
.tracker-card.dead[b-b4e5hnp4oe] {
    border-style: dashed;
    color: var(--bs-secondary-color, #6c757d);
    background: transparent;
}

.tracker-card.dead[b-b4e5hnp4oe] { opacity: 0.55; }

.tracker-card-label[b-b4e5hnp4oe] {
    font-size: 0.85rem;
    line-height: 1.2;
}

.tracker-card-when[b-b4e5hnp4oe] {
    font-size: 0.75rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-top: 0.15rem;
}

/* ── Off-ramp banner ────────────────────────────────────────────────────── */

.tracker-dead-banner[b-b4e5hnp4oe] {
    padding: 0.45rem 0.8rem;
    border-radius: 0.4rem;
    background: color-mix(in srgb, var(--bs-secondary, #6c757d) 12%, transparent);
    border: 1px solid var(--bs-secondary, #6c757d);
    color: var(--bs-body-color, #212529);
    font-size: 0.9rem;
}
/* /Components/Pages/Admin/Onboarding.razor.rz.scp.css */
/* ============================================================================
   ONBOARDING REQUESTS

   Moved out of a raw <style> block in the .razor file. Two reasons, both hit
   while fixing the layout below:

     - Razor parses that block as markup. A CSS comment mentioning an element
       by name had the tags silently stripped from the output.
     - Editing it left the browser's CSSOM stale — the element's text updated
       but its stylesheet kept the rules from an earlier render, so the page
       styled itself from CSS that was no longer on screen anywhere.

   A .razor.css is a real stylesheet: bundled, parsed once, and scoped by the
   framework instead of by the component's lifecycle.
   ============================================================================ */

.onboard-list-item[b-z7igfryzam] { cursor: pointer; }
.onboard-list-item:hover[b-z7igfryzam] { background-color: var(--light-bg); }

/* .list-group-item.active is a Bootstrap pair — solid blue fill AND
   color:#fff. Overriding only the fill left white text on a #e7f1ff
   background: 1.14:1, which made the name of the request you had just
   selected invisible. The colour has to be restated whenever the fill is.

   Tokens rather than hex for all three: the old values were light-theme
   constants that stayed light in dark mode. */
.onboard-list-item.active[b-z7igfryzam] {
    background-color: var(--primary-soft);
    color: var(--primary-ink);
    border-left: 3px solid var(--primary-color);
}

/* .text-muted is a fixed grey that only reads against the page background; on
   the selected row it managed 4.24:1. Inherit the row's own ink instead. No
   opacity step-down — 0.85 put it back under 4.5, and the bold name against
   the smaller lines already carries the hierarchy without spending contrast
   on it. */
.onboard-list-item.active .text-muted[b-z7igfryzam] { color: inherit !important; }

/* Four filters, two columns, at every width.

   This replaced a .btn-group.w-100, which is nowrap by design: the four labels
   need 334px, and in a col-lg-3 header — 194px at a 1200px window — the extra
   140px did not clip, because a .card-header is overflow:visible. It painted
   straight over the detail panel next door.

   A grid rather than a wrapping flex row because four items wrapping by
   available width land on three-per-line more often than not, leaving
   "Rejected" stretched across a row of its own; auto-fit has the same problem,
   since it will happily choose three columns. Two is the only count that
   divides four evenly and can never orphan. Equal widths come free, which is
   what the .btn-group was really providing. */
.onboard-filters[b-z7igfryzam] {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25rem;
}

/* The grid wraps; the labels inside it must not. Without this a squeezed
   "Converted" breaks mid-word and drags the whole strip taller. */
.onboard-filters .btn[b-z7igfryzam] { white-space: nowrap; }

/* btn-sm is 32px, which is fine under a mouse and short of a thumb. */
@media (max-width: 767.98px) {
    .onboard-filters .btn[b-z7igfryzam] { min-height: 44px; }
}

/* Was an inline style:max-height:75vh, which is right beside the detail panel
   and wrong above it. Below lg the two stack, and 75vh of list is three
   quarters of the screen to scroll past before reaching the record you just
   tapped. */
.onboard-list[b-z7igfryzam] { max-height: 40vh; overflow-y: auto; }

@media (min-width: 992px) {
    .onboard-list[b-z7igfryzam] { max-height: 75vh; }
}
/* /Components/Pages/Admin/Pipeline.razor.rz.scp.css */
/* ── Funnel group strip ─────────────────────────────────────────────────── */

.funnel-grid[b-njtey53236] {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 0.5rem;
}

@media (max-width: 1100px) {
    .funnel-grid[b-njtey53236] { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 700px) {
    .funnel-grid[b-njtey53236] { grid-template-columns: repeat(2, 1fr); }
}

.funnel-card[b-njtey53236] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    padding: 0.65rem 0.85rem;
    /* --surface, not --bs-body-bg: every other card on this page (and in the app)
       paints on --surface, and in dark mode the two are different darks —
       rgb(33,37,41) against rgb(30,35,48) — so the funnel strip read as a
       slightly different material sitting next to the job list. */
    background: var(--surface);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-left: 4px solid var(--accent);
    border-radius: 0.5rem;
    text-align: left;
    cursor: pointer;
    transition: box-shadow 120ms ease, background-color 120ms ease;
}

.funnel-card:hover[b-njtey53236] {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10);
}

.funnel-card.selected[b-njtey53236] {
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    outline: 2px solid var(--accent);
    outline-offset: -1px;
}

/* Empty columns stay visible (an empty PRODUCTION column IS information) but
   recede so full columns carry the eye. */
.funnel-card.empty[b-njtey53236] { opacity: 0.6; }

.funnel-card-title[b-njtey53236] {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    /* --accent-ink, not --accent: the accents are fills and fail AA as 11.5px
       text (app.css, PIPELINE GROUP ACCENTS). The fallback keeps this rule
       working if a caller supplies only --accent. */
    color: var(--accent-ink, var(--accent));
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.funnel-card-count[b-njtey53236] {
    font-size: 0.85rem;
    color: var(--bs-secondary-color, #6c757d);
}

.funnel-card-total[b-njtey53236] {
    font-size: 1.25rem;
    font-weight: 700;
}

/* ── Sub-stage drill-down row ───────────────────────────────────────────── */

.substage-row[b-njtey53236] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.6rem;
    border-radius: 0.5rem;
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.substage-card[b-njtey53236] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    min-width: 160px;
    padding: 0.5rem 0.75rem;
    /* --surface for the same reason as .funnel-card above: --bs-body-bg is a
       different dark from the one every card in the app uses. */
    background: var(--surface);
    border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
    border-radius: 0.4rem;
    text-align: left;
    cursor: pointer;
}

.substage-card:hover[b-njtey53236] { box-shadow: 0 1px 6px rgba(0, 0, 0, 0.10); }

.substage-card.selected[b-njtey53236] {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
}

/* Follow-up Due. The soft/ink pair rather than a hand-mixed amber: the tint was
   mixed against --bs-body-bg and the name was a fixed #8a6d00, so in dark mode a
   dark brown label sat on a dark amber tint. Both halves flip together now, which
   is the whole point of keeping the pair. */
.substage-card.nudge[b-njtey53236] {
    border-color: var(--warning-ink);
    background: var(--warning-soft);
}

.substage-card.nudge .substage-name[b-njtey53236] { color: var(--warning-ink); }

.substage-name[b-njtey53236] { font-weight: 600; font-size: 0.85rem; }

.substage-cell[b-njtey53236] {
    font-size: 0.8rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ── Off-ramp row ───────────────────────────────────────────────────────── */

.offramp-row[b-njtey53236] {
    /* MudBlazor's global reset sets all <button> to display:inline-flex, which
       splits this button's mixed text/inline-element content into separate
       anonymous flex items and collapses the whitespace at each one's edges
       (e.g. "Expired: 1 job" renders as "Expired:1job"). Overriding back to
       inline-block restores normal text flow so the spaces survive. */
    display: inline-block;
    width: 100%;
    padding: 0.4rem 0.85rem;
    background: transparent;
    border: 1px dashed var(--bs-border-color, #dee2e6);
    border-radius: 0.4rem;
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.85rem;
    text-align: left;
    cursor: pointer;
}

.offramp-row:hover[b-njtey53236] { background: var(--bs-tertiary-bg, #f8f9fa); }

.offramp-row.selected[b-njtey53236] {
    outline: 2px solid var(--bs-secondary, #6c757d);
    outline-offset: -1px;
}

/* ── Job list ───────────────────────────────────────────────────────────── */

.step-dot[b-njtey53236] {
    display: inline-block;
    width: 0.55rem;
    height: 0.55rem;
    margin-right: 0.35rem;
    border-radius: 50%;
    background: var(--accent);
    vertical-align: baseline;
}

[b-njtey53236] .pipeline-table tr { cursor: pointer; }

/* ── Phone card list ────────────────────────────────────────────────────────
   Content classes only — the card shell, sort bar and pager are .rec-card /
   .rec-cards-* in app.css, shared with All Work, Contacts and the two message
   inboxes. No media query: the container carries d-md-none. */

.pipe-card-top[b-njtey53236] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.pipe-card-wo[b-njtey53236] {
    font-weight: 700;
    font-size: 0.95rem;
}

.pipe-card-who[b-njtey53236] {
    font-size: 0.9rem;
    margin-top: 0.1rem;
    /* A long "Lastname, Firstname — Company" wraps rather than pushing the card
       sideways, but two lines is the ceiling: the step and the value below it
       are what make this card worth scanning. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.pipe-card-city[b-njtey53236] { color: var(--muted-text); }

.pipe-card-step[b-njtey53236] {
    font-size: 0.82rem;
    margin-top: 0.3rem;
}

.pipe-card-age[b-njtey53236] { color: var(--muted-text); }

.pipe-card-foot[b-njtey53236] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-top: 0.35rem;
    font-size: 0.82rem;
    color: var(--muted-text);
}

.pipe-card-value[b-njtey53236] {
    flex: 0 0 auto;
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--dark-text);
}

/* MudChip carries its own margins for use in a chip row; inside a flex header
   they push the work-order number off-centre. */
[b-njtey53236] .pipe-card .mud-chip {
    margin: 0;
    flex: 0 0 auto;
}

/* The phone filter disclosure. .btn-sm is 32px here — the field portal's 44px
   floor is scoped to .field-main and this is the admin shell, so this button
   asks for its own rather than raising every small button in admin (round 1 §5
   declined that, and it still stands). */
@media (max-width: 767.98px) {
    .pipe-filter-toggle[b-njtey53236] {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }
}
/* /Components/Pages/Admin/Settings.razor.rz.scp.css */
/* Rail block labels ("YOUR BUSINESS", "GETTING PAID", …). Small enough to
   read as structure rather than as another row you could click. */
.settings-rail-block[b-3hcuj5cf1a] {
    font-size: .68rem;
    letter-spacing: .06em;
    padding-left: .25rem;
}

/* Compact rail rows: one line, plus an optional status chip. The old rows
   carried a two-line subtitle that also appears in the content header, which
   cost ~120px each and pushed half the categories below the fold. */
.settings-rail .list-group-item[b-3hcuj5cf1a] {
    padding-top: .5rem;
    padding-bottom: .5rem;
}

.settings-rail .badge[b-3hcuj5cf1a] {
    font-size: .65rem;
    font-weight: 600;
}

/* Keep the rail in view while a long category scrolls beside it. Offset clears
   the shell strip — a plain top:0 sticky parks content underneath it. */
@media (min-width: 992px) {
    .settings-rail[b-3hcuj5cf1a] {
        position: sticky;
        top: calc(var(--shell-strip-h, 48px) + 1rem);
    }
}

/* Below lg the rail becomes a horizontally scrolling chip strip instead of a
   full-width stack. Stacked, it put the first field ~1000px down the page —
   every category switch cost a scroll up and a longer scroll back down. */
@media (max-width: 991.98px) {
    .settings-rail[b-3hcuj5cf1a] {
        display: flex;
        gap: .5rem;
        overflow-x: auto;
        padding-bottom: .5rem;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }

    /* Block labels are structure for a vertical list; in a single-row strip
       they only cost height. */
    .settings-rail-block[b-3hcuj5cf1a] {
        display: none;
    }

    .settings-rail > div[b-3hcuj5cf1a] {
        flex: 0 0 auto;
        margin-bottom: 0 !important;
    }

    .settings-rail .list-group[b-3hcuj5cf1a] {
        flex-direction: row;
        gap: .35rem;
        box-shadow: none !important;
    }

    .settings-rail .list-group-item[b-3hcuj5cf1a] {
        white-space: nowrap;
        border-radius: 999px !important;
        border: 1px solid var(--bs-border-color);
        padding: .35rem .75rem;
    }

    /* The active item's subtitle would break the strip's single-line rhythm —
       it still shows in the content header directly below. */
    .settings-rail .list-group-item > div:nth-child(2)[b-3hcuj5cf1a] {
        display: none;
    }
}

/* Search lives in the page header now. Without a floor it collapses to the
   width of its placeholder when the header row has room to spare. */
.settings-search[b-3hcuj5cf1a] {
    min-width: 16rem;
}

@media (min-width: 576px) {
    .settings-search[b-3hcuj5cf1a] {
        width: 22rem;
    }
}

/* Section card header. The title doubles as the collapse control, so it's a
   button reset to look like the heading it replaces — the whole label is the
   hit target, not just the caret. Sections that can't collapse (search
   results, the Unsorted bucket, anything holding unsaved edits) render the
   same button disabled, which keeps one markup path; these overrides stop the
   browser greying it into looking broken. */
.settings-section-toggle[b-3hcuj5cf1a] {
    display: flex;
    align-items: center;
    gap: .4rem;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    text-align: left;
}

.settings-section-toggle:disabled[b-3hcuj5cf1a] {
    cursor: default;
    color: inherit;
    opacity: 1;
}

.settings-section-toggle:not(:disabled):hover .settings-section-caret[b-3hcuj5cf1a] {
    color: var(--bs-primary);
}

/* Fixed width so titles line up whether or not a caret is drawn. */
.settings-section-caret[b-3hcuj5cf1a] {
    flex: 0 0 1rem;
    font-size: .8rem;
    color: var(--bs-secondary-color);
}
/* /Components/Pages/Admin/WorkOrderList.razor.rz.scp.css */
/* Inline-editable triage cells. The dashed underline is the only hint that a
   cell is editable, so it has to read as an affordance without competing with
   the Stage / Next Step chips beside it. */
.wo-edit-cell[b-64gedz1ykv] {
    cursor: pointer;
    border-bottom: 1px dashed var(--mud-palette-text-secondary);
    padding: 1px 3px;
    border-radius: 3px;
    display: inline-block;
}

.wo-edit-cell:hover[b-64gedz1ykv] {
    background-color: var(--mud-palette-action-default-hover);
}

/* The chips inside an editable cell own their own hit target, which would
   otherwise swallow the click meant for the cell. */
.wo-edit-cell .mud-chip[b-64gedz1ykv] {
    pointer-events: none;
    margin: 0;
}

/* ============================================================================
   PHONE CARD LIST (< md)
   Authored mobile-first: the base rules below ARE the phone layout, and the
   whole block is inert on desktop because .wo-cards carries d-md-none. New
   page CSS follows this direction; app.css keeps its older desktop-first
   max-width convention (Docs/MOBILE_FIRST_INTERFACE.md §3.2).
   ============================================================================ */

.wo-card-top[b-64gedz1ykv] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
}

.wo-card-id strong[b-64gedz1ykv] {
    font-size: 0.95rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.wo-card-id strong[b-64gedz1ykv]::before {
    content: "WO #";
    font-weight: 700;
}

.wo-card-kicker[b-64gedz1ykv] {
    display: block;
    font-size: 0.7rem;
    color: var(--muted-text);
}

/* The actions menu is pulled into the card's padding so its 44px hit target
   doesn't steal width from the content beside it. */
.wo-card-top-right[b-64gedz1ykv] {
    display: flex;
    align-items: center;
    gap: 0.15rem;
    margin: -0.35rem -0.4rem 0 0;
}

.wo-card-cust[b-64gedz1ykv] {
    font-size: 0.9rem;
    font-weight: 600;
    margin-top: 0.1rem;
}

.wo-card-loc[b-64gedz1ykv] {
    font-size: 0.76rem;
    color: var(--muted-text);
}

.wo-card-meta[b-64gedz1ykv] {
    font-size: 0.78rem;
    margin-top: 0.25rem;
}

.wo-card-meta .sep[b-64gedz1ykv] {
    color: var(--border-strong);
    margin: 0 0.3rem;
}

.wo-card-meta .amt[b-64gedz1ykv] {
    font-weight: 700;
}

.wo-card-chips[b-64gedz1ykv] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.35rem;
}

/* The next step is advice, not state — dashed, so it can't be mistaken for the
   stage badge or the signing chip sitting beside it. */
.wo-card-step[b-64gedz1ykv] {
    display: inline-flex;
    align-items: center;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.5;
    padding: 0.1rem 0.5rem;
    border-radius: 99px;
    color: var(--primary-ink);
    border: 1px dashed var(--primary-color);
}

/* /Components/Pages/Admin/Workorders.razor.rz.scp.css */
/* ============================================================================
   PHONE QUICK EDIT + JUMP CHIPS (Docs/MOBILE_FIRST_INTERFACE.md §3.5)

   Authored mobile-first: these base rules ARE the phone layout, and the whole
   block is inert on desktop because every element carries d-md-none. app.css
   keeps its older desktop-first max-width convention (§3.2).

   The visit card (.wo-visit*) and the jump chips (.wo-quick*) used to live
   here. They moved to app.css when the subcontractor's job detail became their
   second consumer (mobile-first-plan2.md §11) — scoped CSS is per-component, so
   sharing them from here was not an option and copying them would have left two
   definitions free to drift.
   ============================================================================ */


/* A jump lands the card's top edge at the top of the viewport, which on desktop
   is underneath the sticky strip and the sticky action bar. Clear both, plus a
   little breathing room, so the header the user asked for is actually visible.
   Harmless on phone, where the action bar is docked at the bottom instead. */
[b-8gwop09uw3] .mud-card[id^="wo-card-"],
[b-8gwop09uw3] #payments-panel {
    scroll-margin-top: calc(var(--shell-strip-h) + 4rem);
}

/* ── Contact reach strip ──────────────────────────────────────────────────────
   The one block in this file that is NOT phone-only: it renders inside the full
   form at every width, so it carries no d-md-none and the file header's "inert
   on desktop" note doesn't cover it.

   Written to wrap rather than to a breakpoint. Address, Directions and Call are
   three independent items, so whichever stops fitting drops to the next line on
   its own — no width has to be guessed. */

.wo-reach[b-8gwop09uw3] {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.wo-reach-line[b-8gwop09uw3] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem 0.9rem;
    font-size: 0.9rem;
    line-height: 1.4;
    color: var(--dark-text);
}

.wo-reach-line > i[b-8gwop09uw3] { color: var(--muted-text); }

.wo-reach-addr[b-8gwop09uw3] { font-weight: 600; }

.wo-reach-muted[b-8gwop09uw3] { color: var(--muted-text); }

.wo-reach-link[b-8gwop09uw3] {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    /* Holds the 44px floor for phone full-form, which reaches this strip and has
       no visit card to fall back on. On desktop it makes one taller-than-dense
       row, which is the cheaper side of the trade. */
    min-height: 44px;
    color: var(--primary-ink);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.wo-reach-link:hover[b-8gwop09uw3] {
    color: var(--primary-ink);
    text-decoration: underline;
}

.wo-reach-kind[b-8gwop09uw3] {
    font-size: 0.72rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    opacity: 0.75;
}

.wo-reach-access[b-8gwop09uw3] {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border-radius: var(--radius-sm);
    background: var(--warning-soft);
    color: var(--warning-ink);
    font-size: 0.85rem;
    line-height: 1.4;
}

/* ── Sticky bar: which record is this ────────────────────────────────────────
   Reads as a breadcrumb, not as a heading: the record's real headline is the
   Identity card below, and this line exists to answer "where am I" after four
   screens of scrolling, not to compete with it. */
.wo-bar-identity[b-8gwop09uw3] {
    align-items: baseline;
    gap: 0.4rem;
    min-width: 0;
    /* Takes the slack so the buttons stay right-aligned, and truncates rather
       than wrapping the bar onto a second row — a sticky bar that changes
       height as you type is worse than a clipped name. */
    flex: 1 1 auto;
    overflow: hidden;
    white-space: nowrap;
    padding-inline: 0.35rem;
    font-size: 0.85rem;
}

.wo-bar-who[b-8gwop09uw3] {
    font-weight: 600;
    color: var(--dark-text);
    overflow: hidden;
    text-overflow: ellipsis;
}

.wo-bar-ref[b-8gwop09uw3],
.wo-bar-stage[b-8gwop09uw3] {
    color: var(--muted-text);
    flex: 0 0 auto;
}

.wo-bar-sep[b-8gwop09uw3] {
    color: var(--muted-text);
    opacity: 0.5;
    flex: 0 0 auto;
}

/* The one part of this line worth an accent. Warning tones rather than danger:
   unsaved work is a state to notice, not an error to fear — the record is
   intact and the autosave draft has it. */
.wo-bar-dirty[b-8gwop09uw3] {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    margin-inline-start: 0.35rem;
    padding: 0.05rem 0.45rem 0.05rem 0.15rem;
    border-radius: 999px;
    background: var(--warning-soft);
    color: var(--warning-ink);
    font-size: 0.75rem;
    font-weight: 600;
}
/* /Components/Shared/AreaTabStrip.razor.rz.scp.css */
/* Deliberately identical in weight to WorkViewSwitcher: both answer "where am I
   and what else is here", and giving the two orientation strips different looks
   would suggest they mean different kinds of thing. Quieter than the toolbars
   below it — this is orientation, not a control. */

.area-tabs[b-hok5er7s3q] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .25rem;
    margin-bottom: 1rem;
}

.area-tabs-label[b-hok5er7s3q] {
    font-size: .8rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-right: .5rem;
}

.area-tab[b-hok5er7s3q] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .7rem;
    border-radius: 999px;
    font-size: .85rem;
    line-height: 1.2;
    text-decoration: none;
    color: var(--bs-body-color, #212529);
    border: 1px solid transparent;
    transition: background-color .12s ease, border-color .12s ease;
}

.area-tab:hover[b-hok5er7s3q] {
    background-color: var(--bs-tertiary-bg, #f1f3f5);
    border-color: var(--bs-border-color, #dee2e6);
}

.area-tab.active[b-hok5er7s3q] {
    background-color: var(--bs-primary-bg-subtle, #e7e3fb);
    border-color: var(--bs-primary-border-subtle, #c9c1f5);
    color: var(--bs-primary-text-emphasis, #3d2f9e);
    font-weight: 600;
    cursor: default;
}

.area-tab i[b-hok5er7s3q] {
    font-size: .9rem;
    opacity: .8;
}

/* On phones the area name is the first thing worth dropping — the tabs already
   say what the strip is, and the row needs to stay one line. */
@media (max-width: 575.98px) {
    .area-tabs-label[b-hok5er7s3q] { display: none; }

    .area-tab[b-hok5er7s3q] { padding: .3rem .55rem; }
}
/* /Components/Shared/CustomerWorkHistoryDialog.razor.rz.scp.css */
/* Mud palette variables throughout rather than fixed colours, so the popup
   follows the theme the same way the rest of the shell does. */

.cwh-head[b-zgwnral8w4] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    user-select: none;
}

.cwh-head:hover[b-zgwnral8w4] {
    background: var(--mud-palette-action-default-hover);
}

.cwh-head:focus-visible[b-zgwnral8w4] {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

.cwh-chev[b-zgwnral8w4] {
    flex: 0 0 auto;
    color: var(--mud-palette-text-secondary);
}

.cwh-date[b-zgwnral8w4] {
    flex: 0 0 auto;
    font-size: 0.85rem;
    white-space: nowrap;
}

/* Takes the slack so the amount stays hard right; truncates rather than
   wrapping, because a mixed-trade job can name four of them. */
.cwh-trades[b-zgwnral8w4] {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.85rem;
    color: var(--mud-palette-text-secondary);
}

.cwh-amount[b-zgwnral8w4] {
    flex: 0 0 auto;
    font-weight: 600;
    font-size: 0.9rem;
    white-space: nowrap;
}

.cwh-muted[b-zgwnral8w4] {
    color: var(--mud-palette-text-secondary);
    font-size: 0.8rem;
}

.cwh-body[b-zgwnral8w4] {
    border-top: 1px solid var(--mud-palette-lines-default);
    padding: 0.5rem 0.75rem 0.25rem;
}

.cwh-line + .cwh-line[b-zgwnral8w4] {
    border-top: 1px dashed var(--mud-palette-lines-default);
    margin-top: 0.6rem;
    padding-top: 0.6rem;
}

.cwh-line-head[b-zgwnral8w4] {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    margin-bottom: 0.35rem;
}

.cwh-line-amt[b-zgwnral8w4] {
    margin-left: auto;
    font-weight: 600;
    white-space: nowrap;
}

.cwh-note[b-zgwnral8w4] {
    margin: 0 0 0.5rem 0;
}

.cwh-note-label[b-zgwnral8w4] {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 0.1rem;
}

.cwh-note-body[b-zgwnral8w4] {
    font-size: 0.875rem;
}

/* Only for notes that are plain text. Applying this to markup renders the
   whitespace BETWEEN the tags as blank lines, on top of the spacing the block
   elements already provide — the legacy notes are full of "</div>  <div>", so
   every one of them came out triple-spaced. */
.cwh-note-body.plain[b-zgwnral8w4] {
    white-space: pre-wrap;
}

.cwh-caution[b-zgwnral8w4] {
    color: var(--mud-palette-error);
}

.cwh-nonotes[b-zgwnral8w4] {
    font-style: italic;
}

/* The note bodies are injected as MarkupString, so their elements never carry
   the scope attribute — ::deep is the only way to reach Quill's own markup.
   Left un-styled, its <p> margins double-space every note. */
.cwh-note-body[b-zgwnral8w4]  p {
    margin: 0 0 0.25rem 0;
}

.cwh-note-body[b-zgwnral8w4]  p:last-child {
    margin-bottom: 0;
}

.cwh-note-body[b-zgwnral8w4]  ul,
.cwh-note-body[b-zgwnral8w4]  ol {
    margin: 0 0 0.25rem 0;
    padding-left: 1.25rem;
}

.cwh-note-body[b-zgwnral8w4]  li {
    margin: 0;
}

/* The legacy notes are div-per-line rather than <p>, and lean on
   "<div>&nbsp;</div>" for blank lines. Left alone that reads fine — this just
   stops a trailing spacer adding a gap under the last line of a note. */
.cwh-note-body[b-zgwnral8w4]  div:last-child {
    margin-bottom: 0;
}
/* /Components/Shared/GlobalSearch.razor.rz.scp.css */
/* Cmd-K command palette. Bootstrap-token colours so it tracks light/dark
   alongside the rest of the admin shell. */

.gsearch-overlay[b-d73gk3gwfq] {
    position: fixed;
    inset: 0;
    z-index: 1080; /* above MudBlazor's dialog backdrop-ish range and the navbar */
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding-top: 12vh;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(2px);
}

.gsearch-panel[b-d73gk3gwfq] {
    width: min(640px, 92vw);
    max-height: 70vh;
    display: flex;
    flex-direction: column;
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #1f2937);
    border: 1px solid var(--bs-border-color, #e5e7eb);
    border-radius: 12px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
    overflow: hidden;
}

/* Input row */
.gsearch-input-row[b-d73gk3gwfq] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--bs-border-color, #e5e7eb);
}

.gsearch-input-icon[b-d73gk3gwfq] {
    font-size: 1.05rem;
    color: var(--bs-secondary-color, #6b7280);
}

.gsearch-input[b-d73gk3gwfq] {
    flex: 1;
    border: none;
    outline: none;
    background: transparent;
    color: inherit;
    font-size: 1.05rem;
    padding: 0;
}

.gsearch-input[b-d73gk3gwfq]::placeholder {
    color: var(--bs-secondary-color, #9ca3af);
}

.gsearch-esc[b-d73gk3gwfq] {
    font-size: 0.7rem;
    color: var(--bs-secondary-color, #6b7280);
    background: var(--bs-tertiary-bg, #f3f4f6);
    border-radius: 4px;
    padding: 2px 6px;
}

/* Results */
.gsearch-results[b-d73gk3gwfq] {
    overflow-y: auto;
    padding: 0.4rem 0.4rem 0.6rem;
}

.gsearch-group-heading[b-d73gk3gwfq] {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bs-secondary-color, #6b7280);
    padding: 0.6rem 0.75rem 0.3rem;
}

.gsearch-hit[b-d73gk3gwfq] {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 0.75rem;
    width: 100%;
    text-align: left;
    border: none;
    background: transparent;
    color: inherit;
    padding: 0.5rem 0.75rem;
    border-radius: 8px;
    cursor: pointer;
}

.gsearch-hit-active[b-d73gk3gwfq] {
    background: var(--bs-primary-bg-subtle, #e7e7fb);
}

.gsearch-hit-icon[b-d73gk3gwfq] {
    font-size: 1rem;
    width: 1.3rem;
    text-align: center;
    color: var(--bs-primary, #5b5bd6);
    flex-shrink: 0;
}

.gsearch-hit-text[b-d73gk3gwfq] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.gsearch-hit-title[b-d73gk3gwfq] {
    font-weight: 600;
    font-size: 0.92rem;
    line-height: 1.2;
}

.gsearch-hit-sub[b-d73gk3gwfq] {
    font-size: 0.78rem;
    color: var(--bs-secondary-color, #6b7280);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gsearch-empty[b-d73gk3gwfq] {
    padding: 1.6rem 1rem;
    text-align: center;
    color: var(--bs-secondary-color, #6b7280);
    font-size: 0.9rem;
}

/* Footer hint bar */
.gsearch-footer[b-d73gk3gwfq] {
    display: flex;
    gap: 1.2rem;
    padding: 0.5rem 1rem;
    border-top: 1px solid var(--bs-border-color, #e5e7eb);
    font-size: 0.72rem;
    color: var(--bs-secondary-color, #6b7280);
}

.gsearch-footer kbd[b-d73gk3gwfq] {
    background: var(--bs-tertiary-bg, #f3f4f6);
    color: var(--bs-secondary-color, #6b7280);
    border-radius: 4px;
    padding: 1px 5px;
    margin-right: 3px;
    font-size: 0.72rem;
}
/* /Components/Shared/GlossaryPanel.razor.rz.scp.css */
/* Right-side glossary flyout — same shape as the "?" help panel so the two feel
   like one family. Light scrim you can click to dismiss; theme-aware tokens. */
.gloss-scrim[b-lk5e23r1hh] {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 1065;
    display: flex;
    justify-content: flex-end;
}

.gloss-panel[b-lk5e23r1hh] {
    width: 360px;
    max-width: 100vw;
    height: 100%;
    background: var(--surface);
    color: var(--dark-text);
    border-left: 1px solid var(--border-color);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    animation: gloss-slide-in-b-lk5e23r1hh 0.18s ease;
}

@keyframes gloss-slide-in-b-lk5e23r1hh {
    from { transform: translateX(24px); opacity: 0.4; }
    to { transform: translateX(0); opacity: 1; }
}

.gloss-head[b-lk5e23r1hh] {
    display: flex;
    align-items: center;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--border-color);
}
.gloss-head-title[b-lk5e23r1hh] {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--muted-text);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.gloss-close[b-lk5e23r1hh] {
    margin-left: auto;
    background: transparent;
    border: 0;
    color: var(--muted-text);
    cursor: pointer;
    padding: 4px 6px;
    line-height: 1;
    border-radius: var(--radius-sm);
}
.gloss-close:hover[b-lk5e23r1hh] { background: var(--light-bg); color: var(--dark-text); }

.gloss-body[b-lk5e23r1hh] { flex: 1 1 auto; overflow-y: auto; padding: 1.1rem 1rem; }
.gloss-term[b-lk5e23r1hh] {
    font-size: 1.25rem;
    font-weight: 700;
    margin: 0 0 0.35rem;
    color: var(--dark-text);
}
.gloss-short[b-lk5e23r1hh] {
    font-size: 0.95rem;
    font-weight: 500;
    margin: 0 0 0.6rem;
    color: var(--dark-text);
}
.gloss-long[b-lk5e23r1hh] {
    font-size: 0.88rem;
    line-height: 1.65;
    margin: 0 0 0.9rem;
    color: var(--muted-text);
}

.gloss-see-label[b-lk5e23r1hh] {
    font-size: 0.68rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted-text);
    margin-bottom: 0.4rem;
}
.gloss-see[b-lk5e23r1hh] { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.gloss-see-link[b-lk5e23r1hh] {
    display: inline-flex;
    align-items: center;
    padding: 0.15rem 0.6rem;
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 999px;
    color: var(--primary-color);
    font-size: 0.82rem;
    cursor: pointer;
}
.gloss-see-link:hover[b-lk5e23r1hh] { border-color: var(--primary-color); }

.gloss-foot[b-lk5e23r1hh] {
    border-top: 1px solid var(--border-color);
    padding: 0.7rem 1rem;
}
.gloss-foot a[b-lk5e23r1hh] {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.82rem;
    color: var(--primary-color);
    text-decoration: none;
}
.gloss-foot a:hover[b-lk5e23r1hh] { text-decoration: underline; }

@media (max-width: 575.98px) {
    .gloss-panel[b-lk5e23r1hh] { width: 100vw; border-left: 0; }
}
/* /Components/Shared/HelpPanel.razor.rz.scp.css */
/* Right-side contextual help panel. Non-modal-ish: a light scrim you can click
   to dismiss, with the panel sliding in from the right. Theme-aware via the app
   design tokens. */
.help-scrim[b-mnnsg3ffgc] {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 1060;
    display: flex;
    justify-content: flex-end;
}

.help-panel[b-mnnsg3ffgc] {
    width: 380px;
    max-width: 100vw;
    height: 100%;
    background: var(--surface);
    color: var(--dark-text);
    border-left: 1px solid var(--border-color);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    animation: help-slide-in-b-mnnsg3ffgc 0.18s ease;
}

@keyframes help-slide-in-b-mnnsg3ffgc {
    from { transform: translateX(24px); opacity: 0.4; }
    to { transform: translateX(0); opacity: 1; }
}

.help-head[b-mnnsg3ffgc] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--border-color);
}
.help-head-title[b-mnnsg3ffgc] {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--primary-color);
}
.help-close[b-mnnsg3ffgc] {
    margin-left: auto;
    background: transparent;
    border: 0;
    color: var(--muted-text);
    cursor: pointer;
    padding: 4px 6px;
    line-height: 1;
    border-radius: var(--radius-sm);
}
.help-close:hover[b-mnnsg3ffgc] { background: var(--light-bg); color: var(--dark-text); }

.help-body[b-mnnsg3ffgc] { flex: 1 1 auto; overflow-y: auto; padding: 1rem; }
.help-summary[b-mnnsg3ffgc] { font-size: 0.9rem; line-height: 1.6; margin: 0 0 1rem; }

.help-video[b-mnnsg3ffgc] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 0.75rem;
    margin-bottom: 1rem;
    background: var(--primary-soft, var(--light-bg));
    color: var(--primary-color);
    border-radius: var(--radius-sm);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 600;
}
.help-video:hover[b-mnnsg3ffgc] { filter: brightness(0.97); }

.help-section-label[b-mnnsg3ffgc] {
    font-size: 0.68rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted-text);
    margin: 0.75rem 0 0.35rem;
}

.help-links[b-mnnsg3ffgc] { display: flex; flex-direction: column; gap: 2px; }
.help-link[b-mnnsg3ffgc] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.6rem;
    border-radius: var(--radius-sm);
    color: var(--dark-text);
    text-decoration: none;
    font-size: 0.88rem;
}
.help-link:hover[b-mnnsg3ffgc] { background: var(--light-bg); }
.help-link-ico[b-mnnsg3ffgc] { font-size: 1rem; width: 1.2rem; text-align: center; color: var(--muted-text); flex: 0 0 auto; }
.help-link-text[b-mnnsg3ffgc] { flex: 1 1 auto; min-width: 0; }
.help-link-go[b-mnnsg3ffgc] { font-size: 0.8rem; color: var(--muted-text); opacity: 0; transition: opacity 0.12s ease; }
.help-link:hover .help-link-go[b-mnnsg3ffgc] { opacity: 1; }

.help-foot[b-mnnsg3ffgc] {
    border-top: 1px solid var(--border-color);
    padding: 0.6rem 1rem;
    font-size: 0.72rem;
    color: var(--muted-text);
}
.help-foot kbd[b-mnnsg3ffgc] {
    background: var(--light-bg);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    padding: 0 0.3rem;
    font-size: 0.7rem;
    color: var(--dark-text);
}

@media (max-width: 575.98px) {
    .help-panel[b-mnnsg3ffgc] { width: 100vw; border-left: 0; }
}
/* /Components/Shared/InfoDot.razor.rz.scp.css */
/* Inline info-dot: a muted "ⓘ" that reveals a one-line definition on hover/focus.
   Kept small and low-contrast so it never competes with the label it annotates. */
.info-dot[b-2xsxsj8zkn] {
    position: relative;
    display: inline-flex;
    align-items: center;
    margin-left: 0.25rem;
    vertical-align: baseline;
}

.info-dot-btn[b-2xsxsj8zkn] {
    position: relative;
    display: inline-flex;
    align-items: center;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    color: var(--muted-text);
    font-size: 0.8em;
    line-height: 1;
    text-decoration: none;
    cursor: help;
}

/* The glyph renders 11.2 x 11.8px, because it is sized in em against whatever
   label it annotates. Growing the BOX is what an earlier pass tried and backed
   out of: these sit inline in labels whose line box is ~22px, so a 44px button
   pushes the label off its own baseline — the reason the 44px rule ended up
   scoped to .wo-page-header alone.

   So grow the hit area instead, with a centred overlay that has no layout of
   its own. 24px is WCAG 2.5.8's minimum and roughly doubles the target in each
   direction; 44 is deliberately not used here, because the overlay would reach
   ~16px past the glyph on each side and start swallowing taps meant for the
   label — and a tap on a <label> focuses its input, so stealing it is worse
   than a small target. Inline targets constrained by line-height are exempt
   from 2.5.8 anyway; this is comfort, not conformance. */
.info-dot-btn[b-2xsxsj8zkn]::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 24px;
    height: 24px;
    transform: translate(-50%, -50%);
}
.info-dot-btn:hover[b-2xsxsj8zkn],
.info-dot-btn:focus-visible[b-2xsxsj8zkn] { color: var(--primary-color); }

/* The tooltip — hidden until the dot is hovered or the link is focused. */
.info-dot-tip[b-2xsxsj8zkn] {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    z-index: 1090;
    width: max-content;
    max-width: 240px;
    padding: 0.45rem 0.6rem;
    background: var(--dark-text);
    color: var(--surface);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    font-size: 0.78rem;
    font-weight: 400;
    line-height: 1.45;
    white-space: normal;
    text-align: left;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s ease, transform 0.12s ease;
    pointer-events: none;
}
.info-dot-tip strong[b-2xsxsj8zkn] { font-weight: 600; }

/* A little arrow under the bubble. */
.info-dot-tip[b-2xsxsj8zkn]::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--dark-text);
}

.info-dot:hover .info-dot-tip[b-2xsxsj8zkn],
.info-dot-btn:focus-visible + .info-dot-tip[b-2xsxsj8zkn] {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
}

/* visibility:hidden still leaves the bubble laid out, and an absolutely
   positioned 240px box centred on a dot near the right edge sits past the
   viewport — which widens the page's scroll area even though nothing is
   visible. The Type dot in the work-order editor does this at *every* width:
   69px of horizontal scroll on a 375px phone and on a 1400px desktop alike,
   with no visible cause. display:none takes it out of layout entirely.

   Not scoped to phone, because the bug isn't: a page-wide horizontal scrollbar
   nobody can explain is worth more than the 120ms fade, which can't animate
   out of display:none and so becomes a pop. Hover and keyboard focus both
   still reveal it. */
.info-dot-tip[b-2xsxsj8zkn] {
    display: none;
}

.info-dot:hover .info-dot-tip[b-2xsxsj8zkn],
.info-dot-btn:focus-visible + .info-dot-tip[b-2xsxsj8zkn] {
    display: block;
}
/* /Components/Shared/ProjectGantt.razor.rz.scp.css */
/* Project Scheduler Gantt — sibling of the dispatch board's hand-rolled grid. */

.pg-scroll[b-ktekpjw4j3] {
    overflow-x: auto;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 6px;
    background: var(--bs-body-bg, #fff);
}

.pg-canvas[b-ktekpjw4j3] {
    min-width: 100%;
}

/* ── Headers ── */
.pg-header-months[b-ktekpjw4j3] {
    display: flex;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-tertiary-bg, #f8f9fa);
    position: sticky;
    top: 0;
}

.pg-month[b-ktekpjw4j3] {
    font-size: .75rem;
    font-weight: 600;
    padding: 2px 6px;
    border-right: 1px solid var(--bs-border-color, #dee2e6);
    white-space: nowrap;
    overflow: hidden;
    color: var(--bs-secondary-color, #6c757d);
}

.pg-header-days[b-ktekpjw4j3] {
    display: flex;
    border-bottom: 2px solid var(--bs-border-color, #dee2e6);
    background: var(--bs-tertiary-bg, #f8f9fa);
}

.pg-day[b-ktekpjw4j3] {
    flex: 0 0 auto;
    font-size: .65rem;
    text-align: center;
    color: var(--bs-secondary-color, #6c757d);
    padding: 1px 0 2px;
    border-right: 1px solid rgba(0, 0, 0, .04);
}

.pg-day.is-weekend[b-ktekpjw4j3] { background: rgba(0, 0, 0, .045); }

.pg-day.is-today[b-ktekpjw4j3] { background: #fff3cd; font-weight: 700; color: #664d03; }

/* ── Rows ── */
.pg-body[b-ktekpjw4j3] { position: relative; }

.pg-row[b-ktekpjw4j3] {
    display: flex;
    height: 40px;
    border-bottom: 1px solid rgba(0, 0, 0, .06);
}

.pg-label[b-ktekpjw4j3] {
    flex: 0 0 auto;
    position: sticky;
    left: 0;
    z-index: 3;
    background: var(--bs-body-bg, #fff);
    border-right: 2px solid var(--bs-border-color, #dee2e6);
    padding: 4px 8px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    overflow: hidden;
}

.pg-label-name[b-ktekpjw4j3] {
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pg-label-trade[b-ktekpjw4j3] {
    font-size: .65rem;
    color: var(--bs-secondary-color, #6c757d);
    white-space: nowrap;
}

.pg-track[b-ktekpjw4j3] {
    position: relative;
    display: flex;
    flex: 0 0 auto;
}

.pg-cell[b-ktekpjw4j3] {
    flex: 0 0 auto;
    height: 100%;
    border-right: 1px solid rgba(0, 0, 0, .04);
}

.pg-cell.is-weekend[b-ktekpjw4j3] { background: rgba(0, 0, 0, .035); }

.pg-cell.is-drop[b-ktekpjw4j3] { background: rgba(13, 110, 253, .18); outline: 1px dashed #0d6efd; outline-offset: -1px; }

/* ── Bars ── */
.pg-bar[b-ktekpjw4j3] {
    position: absolute;
    top: 7px;
    height: 26px;
    border-radius: 5px;
    color: #fff;
    font-size: .72rem;
    line-height: 26px;
    padding: 0 8px 0 6px;
    white-space: nowrap;
    overflow: hidden;
    cursor: grab;
    z-index: 2;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
    display: flex;
    align-items: center;
    gap: 4px;
}

.pg-bar.is-dragging[b-ktekpjw4j3] { opacity: .45; }

.pg-bar.is-critical[b-ktekpjw4j3], .pg-milestone.is-critical[b-ktekpjw4j3] { box-shadow: 0 0 0 2px #d32f2f, 0 1px 2px rgba(0,0,0,.25); }

.pg-bar.is-link-source[b-ktekpjw4j3], .pg-milestone.is-link-source[b-ktekpjw4j3] { box-shadow: 0 0 0 2px #6f42c1, 0 1px 2px rgba(0,0,0,.25); }

.pg-bar-text[b-ktekpjw4j3] { overflow: hidden; text-overflow: ellipsis; }

.pg-conflict[b-ktekpjw4j3] { font-size: .8rem; line-height: 1; flex: 0 0 auto; }

.pg-resize[b-ktekpjw4j3] {
    position: absolute;
    right: 0;
    top: 0;
    width: 9px;
    height: 100%;
    cursor: ew-resize;
    border-right: 3px solid rgba(255, 255, 255, .75);
    border-radius: 0 5px 5px 0;
}

.pg-milestone[b-ktekpjw4j3] {
    position: absolute;
    top: 11px;
    width: 18px;
    height: 18px;
    transform: rotate(45deg);
    border-radius: 3px;
    cursor: grab;
    z-index: 2;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}

.pg-baseline[b-ktekpjw4j3] {
    position: absolute;
    bottom: 2px;
    height: 4px;
    border-radius: 2px;
    background: rgba(108, 117, 125, .55);
    z-index: 1;
}

/* ── Overlays ── */
.pg-todayline[b-ktekpjw4j3] {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    background: #dc3545;
    opacity: .55;
    z-index: 2;
    pointer-events: none;
}

.pg-edges[b-ktekpjw4j3] {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
    z-index: 1;
}

.pg-edge[b-ktekpjw4j3] {
    fill: none;
    stroke: #90a4ae;
    stroke-width: 1.6;
    pointer-events: stroke;
}

.pg-edge.is-violated[b-ktekpjw4j3] { stroke: #dc3545; stroke-width: 2.2; stroke-dasharray: 5 3; }

/* ── Toolbar bits ── */
.pg-swatch[b-ktekpjw4j3] {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 3px;
    margin-right: 4px;
    vertical-align: -1px;
}

.pg-diamond-legend[b-ktekpjw4j3] {
    display: inline-block;
    width: 10px;
    height: 10px;
    background: #78909c;
    transform: rotate(45deg);
    border-radius: 2px;
    margin-right: 5px;
    vertical-align: -1px;
}
/* /Components/Shared/WorkOrderNotesPanel.razor.rz.scp.css */
.hub-note-composer[b-kbmey49qv2] {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    padding: 0.6rem;
    background: var(--bs-body-bg, #fff);
}

.hub-note-card[b-kbmey49qv2] {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 0.5rem;
    padding: 0.55rem 0.7rem;
    margin-bottom: 0.6rem;
    background: var(--bs-body-bg, #fff);
}

.hub-note-card.pinned[b-kbmey49qv2] {
    background: color-mix(in srgb, var(--bs-warning, #ffc107) 12%, var(--bs-body-bg, #fff));
    border-color: color-mix(in srgb, var(--bs-warning, #ffc107) 45%, transparent);
}

.hub-note-card.editing[b-kbmey49qv2] {
    border-color: var(--bs-primary, #0d6efd);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--bs-primary, #0d6efd) 20%, transparent);
}

.hub-note-card.legacy[b-kbmey49qv2] {
    background: var(--bs-tertiary-bg, #f8f9fa);
    border-style: dashed;
}

.hub-note-body[b-kbmey49qv2] { font-size: 0.9rem; }
.hub-note-body :last-child[b-kbmey49qv2] { margin-bottom: 0; }

.hub-note-actions[b-kbmey49qv2] { white-space: nowrap; }
.hub-note-actions .btn-link[b-kbmey49qv2] { line-height: 1; }
/* /Components/Shared/WorkViewSwitcher.razor.rz.scp.css */
/* Segmented-control look, deliberately quieter than the page's own toolbars:
   this is orientation ("where am I, what else is there"), not a filter control,
   and it must not compete with the actions right below it. */

.work-views[b-r6zibkbbph] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .25rem;
    margin-bottom: 1rem;
}

.work-views-label[b-r6zibkbbph] {
    font-size: .8rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-right: .5rem;
}

.work-view[b-r6zibkbbph] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .7rem;
    border-radius: 999px;
    font-size: .85rem;
    line-height: 1.2;
    text-decoration: none;
    color: var(--bs-body-color, #212529);
    border: 1px solid transparent;
    transition: background-color .12s ease, border-color .12s ease;
}

.work-view:hover[b-r6zibkbbph] {
    background-color: var(--bs-tertiary-bg, #f1f3f5);
    border-color: var(--bs-border-color, #dee2e6);
}

.work-view.active[b-r6zibkbbph] {
    background-color: var(--bs-primary-bg-subtle, #e7e3fb);
    border-color: var(--bs-primary-border-subtle, #c9c1f5);
    color: var(--bs-primary-text-emphasis, #3d2f9e);
    font-weight: 600;
    cursor: default;
}

.work-view i[b-r6zibkbbph] {
    font-size: .9rem;
    opacity: .8;
}

.work-views-hint[b-r6zibkbbph] {
    font-size: .75rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-left: .5rem;
    cursor: help;
}

/* On phones the label is the first thing worth dropping — the icons and names
   already say what the strip is, and the row needs to stay one line. */
@media (max-width: 575.98px) {
    .work-views-label[b-r6zibkbbph],
    .work-views-hint[b-r6zibkbbph] { display: none; }

    .work-view[b-r6zibkbbph] { padding: .3rem .55rem; }
}
