/* /Components/Layout/MainLayout.razor.rz.scp.css */
/* The page container's `md:p-8` step. The shipped trblazeui.css carries only the utilities the library's
   own components use, and `md:p-8` is not one of them, so the breakpoint lives here. */
@media (min-width: 48rem) {
    .tflens-page[b-5p2n31c6mn] {
        padding: 2rem;
    }
}

/* Fix cycle 1, 2026-09-11 — the page container's inset follows the mockups' `.content` step for step
   (docs/mockups/misses.html: 32px, then 20px ≤900, 16px 12px ≤700, 12px 8px ≤480). At 390 the app sat
   24px in from each edge against the mockups' 8px, so every page body had 32px less width to lay out in
   than the design it is compared against. */
@media (max-width: 56.25rem) {
    .tflens-page[b-5p2n31c6mn] {
        padding: 1.25rem;
    }
}

@media (max-width: 43.75rem) {
    .tflens-page[b-5p2n31c6mn] {
        padding: 1rem 0.75rem;
    }
}

@media (max-width: 30rem) {
    .tflens-page[b-5p2n31c6mn] {
        padding: 0.75rem 0.5rem;
    }
}

#blazor-error-ui[b-5p2n31c6mn] {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss[b-5p2n31c6mn] {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
/* The reconnection states are toggled on `#components-reconnect-modal` by `blazor.web.js` itself as the
   circuit drops, retries and gives up — this component authors the dialog, never the state class. They
   therefore appear in no markup in this repository, which is correct and not the dead-rule defect
   REQ-NFR-021 exists to catch, so they are declared to the guardrail rather than left to look like one.

   REQ-NFR-021 external: components-reconnect-show, components-reconnect-paused,
   components-reconnect-resume-failed, components-reconnect-retrying, components-reconnect-failed */

.components-reconnect-first-attempt-visible[b-jnbe98poa6],
.components-reconnect-repeated-attempt-visible[b-jnbe98poa6],
.components-reconnect-failed-visible[b-jnbe98poa6],
.components-pause-visible[b-jnbe98poa6],
.components-resume-failed-visible[b-jnbe98poa6],
.components-rejoining-animation[b-jnbe98poa6] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-retrying[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-failed[b-jnbe98poa6],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-jnbe98poa6] {
    display: block;
}


#components-reconnect-modal[b-jnbe98poa6] {
    background-color: white;
    width: 20rem;
    margin: 20vh auto;
    padding: 2rem;
    border: 0;
    border-radius: 0.5rem;
    box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
    animation: components-reconnect-modal-fadeOutOpacity-b-jnbe98poa6 0.5s both;
    &[open]

{
    animation: components-reconnect-modal-slideUp-b-jnbe98poa6 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity-b-jnbe98poa6 0.5s ease-in-out 0.3s;
    animation-fill-mode: both;
}

}

#components-reconnect-modal[b-jnbe98poa6]::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
    animation: components-reconnect-modal-fadeInOpacity-b-jnbe98poa6 0.5s ease-in-out;
    opacity: 1;
}

@keyframes components-reconnect-modal-slideUp-b-jnbe98poa6 {
    0% {
        transform: translateY(30px) scale(0.95);
    }

    100% {
        transform: translateY(0);
    }
}

@keyframes components-reconnect-modal-fadeInOpacity-b-jnbe98poa6 {
    0% {
        opacity: 0;
    }

    100% {
        opacity: 1;
    }
}

@keyframes components-reconnect-modal-fadeOutOpacity-b-jnbe98poa6 {
    0% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.components-reconnect-container[b-jnbe98poa6] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
}

#components-reconnect-modal p[b-jnbe98poa6] {
    margin: 0;
    text-align: center;
}

#components-reconnect-modal button[b-jnbe98poa6] {
    border: 0;
    background-color: #6b9ed2;
    color: white;
    padding: 4px 24px;
    border-radius: 4px;
}

    #components-reconnect-modal button:hover[b-jnbe98poa6] {
        background-color: #3b6ea2;
    }

    #components-reconnect-modal button:active[b-jnbe98poa6] {
        background-color: #6b9ed2;
    }

.components-rejoining-animation[b-jnbe98poa6] {
    position: relative;
    width: 80px;
    height: 80px;
}

    .components-rejoining-animation div[b-jnbe98poa6] {
        position: absolute;
        border: 3px solid #0087ff;
        opacity: 1;
        border-radius: 50%;
        animation: components-rejoining-animation-b-jnbe98poa6 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
    }

        .components-rejoining-animation div:nth-child(2)[b-jnbe98poa6] {
            animation-delay: -0.5s;
        }

@keyframes components-rejoining-animation-b-jnbe98poa6 {
    0% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    4.9% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 0;
    }

    5% {
        top: 40px;
        left: 40px;
        width: 0;
        height: 0;
        opacity: 1;
    }

    100% {
        top: 0px;
        left: 0px;
        width: 80px;
        height: 80px;
        opacity: 0;
    }
}
/* /Components/Pages/AddSource.razor.rz.scp.css */
/* Add source (REQ-UI-040) — the page's own rules.

   These moved out of Repos.razor.css on 2026-08-28 with the markup they style, when Add source
   stopped being a dialog and became a route (REQ-UI-044). The `max-height: 68vh; overflow-y: auto`
   scroll box that used to stand in for a scrolling dialog body (TR-019 / TR-011b) went with the
   dialog rather than moving, and nothing replaced it: a page scrolls by itself, so a nested
   max-height scroller is exactly the thing that used to hide the Import button below the fold on a
   short viewport. TrBlazeUI 2.1.0-ci.10 gives `DialogContent` a scrolling body of its own, which
   would make the rule unnecessary even if this were still a dialog. */

/* The page's vertical rhythm. TrBlazeUI's shipped CSS carries `gap-4` but no `gap-6`, and the
   mockup's sections sit 24px apart, so the one measurement the page needs lives here. */
.tflens-stack[b-qjkos7xyyu] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

/* The drop zone. Dashed, so it reads as a target rather than a card, and it keeps its own text —
   the state is never carried by colour alone (REQ-NFR-005). */
.tflens-drop[b-qjkos7xyyu] {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.375rem;
    padding: 1.25rem 1rem;
    border: 1px dashed var(--border);
    border-radius: var(--radius, 0.625rem);
    color: var(--foreground);
}

.tflens-drop[data-drop-active="true"][b-qjkos7xyyu] {
    border-style: solid;
    border-color: var(--ring);
    background: var(--accent);
}

/* The preview's four columns fit at 1280 on the grid's own `Density="DataTableDensity.Compact"`
   and the columns' own `whitespace-nowrap` — see AddSource.razor. The `::deep` padding and nowrap
   rules that used to stand in for both are gone with the 2.1.0-ci.10 uptake (TR-025 / TR-031).

   Only the type size is left here: `Compact` sets the padding, not the font, and the mockup's
   preview table is 12px against the page's 14px. The wrapper still scrolls horizontally, which is
   what carries the four columns at 390. */
.tflens-preview-table[b-qjkos7xyyu]  table {
    font-size: 0.75rem;
}

/* Visually hidden but still focusable and clickable: the label sits on the zone, and the button
   opens it, so the input itself must stay in the accessibility tree. */
.tflens-file-input[b-qjkos7xyyu] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
/* /Components/Pages/Auth/Profile.razor.rz.scp.css */
/*
    Two cards side by side on a wide screen, stacked under md.

    TR-001 is closed and the `--alert-*` block that used to head `.profile-page` is gone: the shipped
    trblazeui.css declares every alert token itself, and the four status hues the mockups draw are
    declared once in wwwroot/app.css rather than a third time here. The role badge below reads them
    from there.

    TR-002 is closed and the `min-width: 768px` block that used to sit here is gone. Both of its
    declarations have utilities in the pre-built sheet now, so they are stated on the elements in
    Profile.razor as `p-6 md:p-8` and `grid-cols-1 md:grid-cols-2`. Their BASE halves had to move with
    them: a scoped `.profile-page[b-…]` rule outranks a `.md\:p-8` utility, so leaving `padding` or
    `grid-template-columns` here would have made the new utilities silently inert — the failure this
    whole pass is meant to avoid. What is left below is only what no utility expresses.
*/

.profile-page[b-we19aou8pp] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    /* 75rem is not a step on the max-w scale (max-w-6xl is 72rem), so this one stays here. */
    max-width: 75rem;
    margin: 0 auto;
}

.profile-grid[b-we19aou8pp] {
    display: grid;
    align-items: start;
    gap: 1.5rem;
}

/* Why the columns are `minmax(0, 1fr)` and not `1fr`: a grid item's default `min-width: auto` refuses
   to shrink below the intrinsic width of its content, so the profile card (which holds a DataTable
   ~437px wide) pushed the page sideways at a 390px viewport even though the grid had correctly
   collapsed to one column. Constraining the TRACK fixes it at the container; a
   `.profile-grid > * { min-width: 0 }` rule does not, because scoped CSS cannot reach a child
   component's root element.
   `grid-cols-1` / `md:grid-cols-2` are `repeat(n, minmax(0, 1fr))` by definition, so moving these to
   utilities keeps the minmax — it is not lost with the hand-written rule. */

/* minmax(0, 1fr) constrains the TRACK, but the grid ITEM still carries `min-width: auto` and can
   overflow it. The item here is a <Card>, a child component whose root element carries no scope id
   of this page, so the rule only reaches it through ::deep. Both halves are needed: the track so the
   column can be narrow, and the item so it actually is. */
.profile-grid[b-we19aou8pp]  > * {
    min-width: 0;
    max-width: 100%;
}

/* Anything wide inside a card (the profile key/value table) scrolls in its own box rather than
   widening the card. */
.profile-grid[b-we19aou8pp]  table {
    max-width: 100%;
}

/* The key/value table inside the profile card has an intrinsic minimum around 344px and sat in no
   scrolling container, so it — not the card, and not the grid — was the last thing widening the
   page at 390px.
   `display: block; overflow-x: auto` on the table itself does NOT fix this: the internal row boxes
   keep their min-content width and simply overflow the block. A fixed layout is the right tool for
   a two-column key/value table — the columns compress and long values wrap, so nothing scrolls and
   nothing is hidden. */
.profile-grid[b-we19aou8pp]  table {
    table-layout: fixed;
    width: 100%;
    max-width: 100%;
}

.profile-grid[b-we19aou8pp]  th,
.profile-grid[b-we19aou8pp]  td {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* The card header is a justify-between row: identity on the left, role badge on the right. The email
   is a single long unbreakable-looking token, and a flex item defaults to `min-width: auto`, so the
   identity block refused to shrink and pushed the badge past the card edge — the badge was the last
   thing widening the page at 390px. Letting the block shrink and the text wrap keeps both inside. */
.profile-identity[b-we19aou8pp],
.profile-identity-text[b-we19aou8pp] {
    min-width: 0;
}

/* `break-word`, not `anywhere`, and for the same reason the kv cell below gives: `anywhere` breaks
   inside a word even when the word would fit on a line of its own, which is what split the address in
   the card header as `tflensdemo@techierathore.co` / `m` at 1280 — a width that has room for all of
   it. `break-word` breaks only a word that genuinely cannot fit, so the email stays whole wherever it
   fits and still wraps rather than overflowing on a phone. The two places this page shows the address
   now behave identically. */
.profile-identity-text[b-we19aou8pp]  * {
    overflow-wrap: break-word;
    word-break: normal;
}

/* REQ-UI-005 — the profile key/value card is headerless, as docs/mockups/profile.html draws it: the
   left cell IS the label, so a "Field | Value" header row only repeats it. TR-012 is closed and the
   `::deep thead { display: none }` rule that used to stand here is deleted — `DataTable` takes
   `ShowHeader="false"`, which is set in Profile.razor. Suppressing the element rather than hiding it
   also means no empty `thead` box for `table-layout: fixed` to size columns against.

   There is deliberately NO `Density` here (TR-025): this table never carried a `::deep th, td` padding
   hack, so there is nothing to take up, and the column widths below are set as percentages of a fixed
   layout rather than by padding. */

/* `table-layout: fixed` above splits the columns 50/50, which is why the email wrapped mid-word:
   `tflensdemo@techierat` / `hore.com`. The label side never needs half the card. */
.profile-kv[b-we19aou8pp]  td:first-child {
    width: 34%;
    color: var(--muted-foreground);
}

.profile-kv[b-we19aou8pp]  td:last-child {
    width: 66%;
}

/* `overflow-wrap: anywhere` (inherited from the .profile-grid rule above) breaks inside a word even
   when the word would fit on a line of its own — that is what split the address. `break-word` breaks
   only a word that genuinely cannot fit, so the email stays whole at every width that can hold it and
   still wraps rather than overflowing on a phone. */
.profile-kv[b-we19aou8pp]  td {
    overflow-wrap: break-word;
    word-break: normal;
}

/* Role / Identity provider: badge first, then the caption the mockup sets beside it. */
.profile-badge-row[b-we19aou8pp] {
    display: inline-flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* TR-016 — `.profile-badge-role` is deleted, not repaired. It was measured INERT on 2026-09-12: the
   class did reach the DOM (it merges through `cn`), but it lands on `Badge`'s own root, which carries
   no scope attribute of this page, so `.profile-badge-role[b-…]` could never match and both role
   pills rendered plain `Secondary` — `oklch(0.985 0 0)` on `oklch(0.269 0 0)`, border transparent —
   where docs/mockups/profile.html draws them green. `BadgeVariant.Success` ships now and reaches the
   component, so the colour is carried by a parameter instead of a rule that cannot land. */

/* The mockup's role badge is a green pill with the tick spaced off the word; the built one ran them
   together as "✓Manager". */
.profile-badge-tick[b-we19aou8pp] {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
/* /Components/Pages/Effort.razor.rz.scp.css */
/* The Phase effort page's layout.

   The shipped trblazeui.css carries the responsive variants (sm/md/lg/xl/2xl at 40/48/64/80/96rem)
   and defines --chart-1..5 and the --alert-* tokens in :where(:root) / :where(.dark), so the grids
   that only need a breakpoint are utility classes on the markup and no colour token is re-declared
   here (the app's own palette lives once, in wwwroot/app.css). What is left in this file is what the
   pre-built sheet still cannot express: arbitrary values (the routing/cost pair's 1.3fr / 1fr, the
   mockup's 12.5px mono, the pinned column widths) and the ::deep rules that reach a child
   component's own root. */

.tflens-effort[b-ba2vancesw] {
    min-width: 0;
}

/* The header's period select — the same 12rem the Misses filter uses, so the two report pages that
   carry a window filter present it identically. */
.tflens-period[b-ba2vancesw] {
    width: 12rem;
    max-width: 100%;
}

/* The mockup's `h3` band headings (18px/600). These stay plain `<h3>` markup so the parity gate can
   match the mockup element for element; TypographyH3's own step is 24px and its root would carry
   Typography's scope attribute, not this file's, so the metric would have to travel through ::deep
   anyway. The 18px/600 is an arbitrary value the pre-built sheet does not emit. */
.tflens-band-heading[b-ba2vancesw] {
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
}

/* REQ-UI-046 — the five-tile KPI row.

   StatGroup is deliberately not used: its shim spans tiles on TrBlazeUI's 12-column grid and twelve
   does not divide by five, so a five-tile row lands 4 + 1. The design puts five equal tiles across at
   1280, two across in the middle, and one per row at 390 — which is `grid-cols-1 md:grid-cols-2
   lg:grid-cols-5` on the markup. Only the two properties no utility carries are left here. */
.tflens-effort-kpis[b-ba2vancesw] {
    align-items: stretch;
    min-width: 0;
}

.tflens-effort-denominators[b-ba2vancesw] {
    align-items: start;
    min-width: 0;
}

/* The routing/cost pair is an arbitrary ratio — the mockup's own 1.3fr / 1fr — so it is not a
   utility and stays a media query. */
.tflens-effort-lower[b-ba2vancesw] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 1rem;
    min-width: 0;
}

@media (min-width: 64rem) {
    .tflens-effort-lower[b-ba2vancesw] {
        grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    }
}

/* REQ-UI-046 — the fan-out tile's accent border, which is what marks it as the coverage figure the
   whole fan-out half of the page is bounded by (BRD-151). `Class` lands on StatTile's own Card root,
   which carries StatTile's scope attribute rather than this page's, so the rule reaches it through
   ::deep from the row this page does own. */
.tflens-effort-kpis[b-ba2vancesw]  .tflens-kpi-accent {
    border-color: color-mix(in oklab, var(--chart-4) 45%, transparent);
}

/* The heaviest-phase tile carries a command rather than a number, and a command is longer than a
   figure — the design drops it to 22px so `day1-greenfield` holds one line at 1280. */
.tflens-kpi-cmd[b-ba2vancesw] {
    font-size: 1.375rem;
    line-height: 1.2;
    white-space: nowrap;
}

/* REQ-UI-046 — the mockup keeps a KPI value on one line (`.grid.g5 .kpi .v { white-space: nowrap }`),
   but a command is longer than a figure: at 1280 `framework-reset` needed 190px, was given 129px and
   broke at its hyphen as `framework-` / `reset`. The box is a size container, so `cqi` is the tile's
   own width, and each fit class sizes a name of up to N characters to it (a monospace glyph measures
   0.575em here; 0.6 leaves a margin). A name that fits keeps the design's 22px, because the clamp's
   ceiling is 22px; one past the last bucket may wrap at its hyphens rather than run out of the tile. */
.tflens-kpi-cmd-box[b-ba2vancesw] {
    display: block;
    container-type: inline-size;
}

.tflens-kpi-cmd-fit-12[b-ba2vancesw] {
    font-size: clamp(0.8125rem, calc(100cqi / 7.2), 1.375rem);
}

.tflens-kpi-cmd-fit-14[b-ba2vancesw] {
    font-size: clamp(0.8125rem, calc(100cqi / 8.4), 1.375rem);
}

.tflens-kpi-cmd-fit-16[b-ba2vancesw] {
    font-size: clamp(0.8125rem, calc(100cqi / 9.6), 1.375rem);
}

.tflens-kpi-cmd-fit-20[b-ba2vancesw] {
    font-size: clamp(0.8125rem, calc(100cqi / 12), 1.375rem);
}

.tflens-kpi-cmd-long[b-ba2vancesw] {
    font-size: 0.875rem;
    white-space: normal;
}

/* Card headers on this page put a badge or a chip opposite the title; the shim reproduces the
   mockup's `.chip`, whose rules live in StatTile's own scope and so cannot reach an element written
   here (the same trap Harness.razor.css documents). */
.tflens-stat-chip[b-ba2vancesw] {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    flex: none;
    border-radius: calc(var(--radius, 0.625rem) - 2px);
}

.tflens-accent-2[b-ba2vancesw] {
    background: color-mix(in oklab, var(--chart-2) 15%, transparent);
    color: var(--chart-2);
}

/* Classes that land on a CHILD COMPONENT's root — Badge, Alert, CardFooter, Card — reach it only
   through ::deep. Blazor stamps every rule in a scoped sheet with THIS file's scope attribute, and a
   component's own root element carries ITS file's attribute instead, so a bare `.tflens-nowrap-badge`
   rule matches nothing at all. It is the same trap Harness.razor.css documents for the stat chips:
   the class is on the element, the rule simply never applies. ::deep re-scopes the ANCESTOR — the
   page root, which this file does own — and leaves the descendant unscoped. */

/* Badge holds its label on one line by itself now (TR-029), so all this still says is that a badge
   is never the flex item that gives up width to its neighbours. */
.tflens-effort[b-ba2vancesw]  .tflens-nowrap-badge {
    flex: none;
}

/* BRD-146 — the token band's denominator rides on the band label, which is upper-cased; the badge
   inside it is a sentence and keeps its own case. */
.tflens-effort[b-ba2vancesw]  .tflens-panel-badge {
    text-transform: none;
    letter-spacing: normal;
}

.tflens-effort[b-ba2vancesw]  .tflens-panel-alert {
    margin-bottom: 0.25rem;
}

/* CardFooter justifies its children to the end; this page's footers carry a paragraph of standing
   copy, which the design sets flush left. The design's `.card-f` also draws a 1px top rule and pads
   16px / 24px (`docs/mockups/effort.html:57`) — that rule is the one line between a table and its
   note, which is why the tables above these footers drop their last row's own border
   (`.tflens-table-flush`) and their CardContent's bottom padding (`.tflens-flush-bottom`). */
.tflens-effort[b-ba2vancesw]  .tflens-card-note {
    display: block;
    text-align: left;
    border-top: 1px solid var(--border);
    padding: 1rem 1.5rem;
}

/* The design's `.mono` is 12.5px wherever it sits (`docs/mockups/effort.html:15`); inside a 13px note
   an inherited 13px mono ran each note a line longer than the design at 390. */
.tflens-card-note .font-mono[b-ba2vancesw] {
    font-size: 0.78125rem;
}

.tflens-effort[b-ba2vancesw]  .tflens-flush-bottom {
    padding-bottom: 0;
}

/* The mockup's `.tbl tr:last-child td { border-bottom: 0 }`: the footer's rule closes the table. */
.tflens-table-flush[b-ba2vancesw]  tbody tr:last-child {
    border-bottom-width: 0;
}

/* A card header's title block and its pill wrap onto two rows below 700px, as the design's
   `.card-h { flex-wrap: wrap; gap: 8px }` does — otherwise the pill squeezes the title to a column
   two words wide at 390. */
@media (max-width: 43.75rem) {
    /* The class now rides on CardHeader's own root (TR-015), which carries CardHeader's scope
       attribute rather than this file's, so it is reached through ::deep. */
    .tflens-effort[b-ba2vancesw]  .tflens-card-head {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    /* The design's `.card-f { padding-left: 16px; padding-right: 16px }` below 700px. */
    .tflens-effort[b-ba2vancesw]  .tflens-card-note {
        padding-inline: 1rem;
    }
}

.tflens-nowrap[b-ba2vancesw] {
    white-space: nowrap;
}

/* The mockup's `.sm` (13px) and `.xs` (12px) against the page's own 14px. These are arbitrary values,
   which the pre-built sheet still does not emit, so they are written out. */
.tflens-small[b-ba2vancesw] {
    font-size: 0.8125rem;
    line-height: 1.25rem;
}

.tflens-xs[b-ba2vancesw] {
    font-size: 0.75rem;
    line-height: 1.1rem;
}

/* The mockup draws its tables in a `card-c flush` — zero padding, so the rows run edge to edge and
   their separators reach the card's borders. CardContent carries `p-6`, which insets the table by
   24px a side; the negative margin restores the design, and the cell padding restores the inset. A
   wide table scrolls inside this box rather than widening the page (BRD-144). */
.tflens-table[b-ba2vancesw] {
    min-width: 0;
    /* The negative margins widen the box by 3rem; a 100% cap cancelled the right-hand one, leaving
       a 24px gap on the right and taking 48px from a table that needs them at 1280. */
    max-width: calc(100% + 3rem);
    overflow-x: auto;
    margin-inline: -1.5rem;
}

    .tflens-table[b-ba2vancesw]  table {
        width: 100%;
    }

    /* DataTable wraps its table in `relative w-full overflow-auto rounded-md border`, drawing a second
       bordered panel inside the card that already holds it. `docs/mockups/effort.html` puts its tables
       in a `card-c flush` and draws no such box — the card's own border is the only one. The wrapper is
       kept, because it is what scrolls a wide table inside its own container (BRD-144); only its
       decoration is removed. */
    .tflens-table[b-ba2vancesw]  .rounded-md.border {
        border: 0;
        border-radius: 0;
    }

    /* TR-025 — the padding override is GONE. Nine columns of live figures (`157h 53m`, `64 of 107`,
       `not observed`) are wider than the mockup's sample, and at DataTable's comfortable `p-4` the
       phase table measured 996px in the 958px card at 1280, hiding Fan-out behind the scroll. That
       was a `::deep th, td { padding }` hack; `Density="DataTableDensity.Compact"` on the table is
       the parameter for it now (`h-9 px-2.5` header, `px-2.5 py-2` body). Only the alignment the
       density does not set is left here. */
    .tflens-table[b-ba2vancesw]  th,
    .tflens-table[b-ba2vancesw]  td {
        vertical-align: middle;
    }

    /* REQ-UI-047 — the mockup's `.tbl th { white-space: nowrap }` and `.tbl td .mono { nowrap }`
       (`docs/mockups/effort.html:71` and `:182`). Without them the phase table at 1280 printed
       `framework-` / `reset`, `deploy-` / `checklist` and every two-word header on two lines. A
       command or a `tokens_scope` bucket is an identifier: it keeps its width. The header carries the
       mockup's 12px/500 and the identifier the mockup's 12.5px `.mono` (`:15`): at DataTable's 14px
       the nine one-line headers made the phase table 1046px at 1280, and the `tokens_scope` buckets
       pushed the Token window card's meaning column out of view. */
    .tflens-table[b-ba2vancesw]  th {
        white-space: nowrap;
        font-size: 0.75rem;
        font-weight: 500;
    }

    .tflens-table[b-ba2vancesw]  td .font-mono {
        white-space: nowrap;
        overflow-wrap: normal;
        font-size: 0.78125rem;
    }

    /* REQ-UI-046 (2026-11-01) — a command longer than 20 characters (`facilitate-brainstorming-session`)
       held on one line pushed the Fan-out column 48px behind a scroll at 1280. Such a name may break
       at its hyphens; every shorter identifier keeps the one-line rule above. */
    .tflens-table[b-ba2vancesw]  td .font-mono.tflens-long-id {
        white-space: normal;
    }

/* TR-012 — the fan-out card's table is headerless in the mockup (its left cell IS the label), and
   `ShowHeader="false"` on the DataTable now suppresses the thead outright, so no rule is needed here.

    /* Three columns in a half-width card: the description takes what is left, and the bar and the
       count are fixed so the count cannot be clipped. A coverage figure the reader cannot read is
       the same as one that is not there, and this card exists to be read (BRD-147). */
    .tflens-kv-table[b-ba2vancesw]  td:nth-child(2) {
        width: 6rem;
    }

    .tflens-kv-table[b-ba2vancesw]  td:nth-child(3) {
        width: 5.5rem;
        white-space: nowrap;
    }

    .tflens-kv-table .tflens-bar[b-ba2vancesw] {
        width: 4.5rem;
    }

/* At 390 the three columns summed wider than the card, and the count — the figure this card exists
   to publish — sat behind the horizontal scroll. The design keeps all three in view at 390 by letting
   the bar shrink to a stub (`docs/mockups/effort.html`, fan-out card at 390), so this does the same:
   a narrower bar and 8px cell insets, and the identifier and the count keep their full width. */
@media (max-width: 43.75rem) {
    .tflens-kv-table[b-ba2vancesw]  td {
        padding-inline: 0.5rem;
    }

    .tflens-kv-table[b-ba2vancesw]  td:nth-child(2) {
        width: 3rem;
    }

    .tflens-kv-table[b-ba2vancesw]  td:nth-child(3) {
        width: 5rem;
    }

    .tflens-kv-table .tflens-bar[b-ba2vancesw] {
        width: 2rem;
        min-width: 1.5rem;
    }
}

/* The design's `p.muted.sm.row`: the info glyph in its own column beside the text, 8px apart and
   3px down to sit on the first line (`docs/mockups/effort.html:180`); below 900px the paragraph is a
   block and the glyph floats beside the first line (`:187`). The glyph is LucideIcon's own `svg`,
   which carries no scope attribute of this file's, so it is reached through ::deep — the plain
   `.tflens-card-note svg` rule that stood here matched nothing, and Tailwind's `svg { display: block }`
   put the glyph on a line of its own above the note. */
.tflens-note-row[b-ba2vancesw] {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
}

.tflens-effort[b-ba2vancesw]  .tflens-note-row > svg {
    flex: none;
    margin-top: 0.1875rem;
}

@media (max-width: 56.25rem) {
    .tflens-note-row[b-ba2vancesw] {
        display: block;
    }

    .tflens-effort[b-ba2vancesw]  .tflens-note-row > svg {
        float: left;
        margin: 0.1875rem 0.5rem 0 0;
    }
}

/* Share bars. `IndicatorClass` lands on an element inside Progress, which carries Progress's scope
   attribute; ::deep from a wrapper this page owns is what lets these colours reach it. */
.tflens-bar[b-ba2vancesw] {
    display: block;
    width: 7rem;
    max-width: 100%;
    min-width: 3rem;
}

    .tflens-bar[b-ba2vancesw]  .tflens-fill-1 {
        background: var(--chart-1);
    }

    .tflens-bar[b-ba2vancesw]  .tflens-fill-2 {
        background: var(--chart-2);
    }

    .tflens-bar[b-ba2vancesw]  .tflens-fill-4 {
        background: var(--chart-4);
    }

    .tflens-bar[b-ba2vancesw]  .tflens-fill-5 {
        background: var(--chart-5);
    }

    /* `unknown` is neither good nor bad — it said nothing — so its bar is the muted foreground. */
    .tflens-bar[b-ba2vancesw]  .tflens-fill-muted {
        background: var(--muted-foreground);
    }

.tflens-bar-row[b-ba2vancesw] {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 0;
}

/* REQ-UI-048 — the per-phase disclosures. */
.tflens-detail-stack[b-ba2vancesw] {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    min-width: 0;
}

.tflens-detail-trigger[b-ba2vancesw] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    min-width: 0;
    font-size: 0.875rem;
    font-weight: 500;
    text-align: left;
}

    .tflens-detail-trigger > span[b-ba2vancesw] {
        min-width: 0;
        overflow-wrap: break-word;
    }

    /* REQ-UI-048 — the phase name is an unhyphenatable identifier, and `break-word` on every span let
       the flex row starve it: at 390 `build-phase` needed 93px, was given 45px, and rendered as
       `build` / `-` / `phase` on three lines. The name keeps its intrinsic width; the prose summary
       beside it is the thing that may shrink and wrap, because prose is what wrapping is for. */
    .tflens-detail-trigger > span.font-mono[b-ba2vancesw] {
        flex: none;
        white-space: nowrap;
        overflow-wrap: normal;
    }

/* At a phone width the trigger's four parts cannot all sit on one line without one of them being
   crushed, so the row wraps instead — the same narrow-width move `docs/mockups/effort.html` makes on
   its own header rows (`.card-h { flex-wrap: wrap }` at max-width 700). */
@media (max-width: 43.75rem) {
    .tflens-detail-trigger[b-ba2vancesw] {
        flex-wrap: wrap;
    }
}

.tflens-spacer[b-ba2vancesw] {
    flex: 1;
}

/* REQ-UI-048 — the mockup's `.coll .trg { padding: 14px 16px }` and `.coll .body { padding: 0 16px
   16px }`. The disclosure sits straight in a Card with no CardContent, so without these the trigger
   and the four dashed bands ran onto the card's own border. Scoped to the per-phase stack: the routing
   card's "By command phase" disclosure reuses the trigger class inside CardContent's own padding. */
.tflens-detail-stack .tflens-detail-trigger[b-ba2vancesw] {
    padding: 0.875rem 1rem;
}

.tflens-detail-stack .tflens-detail-body[b-ba2vancesw] {
    padding: 0 1rem 1rem;
}

.tflens-detail-body[b-ba2vancesw] {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding-top: 0.75rem;
    min-width: 0;
}

/* The mockup's `.panel` — a dashed box per band, in the fixed order Time · Tokens · By model ·
   Fan-out (REQ-UI-048). */
.tflens-panel[b-ba2vancesw] {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    border: 1px dashed var(--border);
    border-radius: var(--radius, 0.625rem);
    padding: 1rem;
    min-width: 0;
    overflow-x: auto;
}

.tflens-panel-label[b-ba2vancesw] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted-foreground);
}

/* The small tables inside a band. These are plain markup rather than DataTable: they are four fixed
   rows of a record this page already holds, and the design draws them as the mockup's `.tbl`. */
.tflens-panel-table[b-ba2vancesw] {
    min-width: 0;
    max-width: 100%;
    overflow-x: auto;
}

    .tflens-panel-table table[b-ba2vancesw] {
        width: 100%;
        border-collapse: collapse;
        font-size: 0.875rem;
    }

    .tflens-panel-table th[b-ba2vancesw] {
        text-align: left;
        font-size: 0.75rem;
        font-weight: 500;
        color: var(--muted-foreground);
        padding: 0.5rem 0.75rem;
        border-bottom: 1px solid var(--border);
        white-space: nowrap;
    }

    .tflens-panel-table td[b-ba2vancesw] {
        padding: 0.625rem 0.75rem;
        border-bottom: 1px solid var(--border);
        vertical-align: middle;
    }

    .tflens-panel-table tr:last-child td[b-ba2vancesw] {
        border-bottom: 0;
    }

    .tflens-panel-table .tflens-right[b-ba2vancesw] {
        text-align: right;
        font-variant-numeric: tabular-nums;
    }

    /* REQ-UI-048 — the mockup states this rule outright (`docs/mockups/effort.html:178`:
       `.tbl td .mono, .tbl th .mono { overflow-wrap: normal; white-space: nowrap }`) and the build
       never carried it. With `table { width: 100% }` the By-model column was starved at 390 —
       `claude-opus-5` needed 99px in a 59px cell and broke mid-token, which is the TF-008 class: the
       text is all present and overlaps nothing, so neither §4a nor §4b can see it. A model id is an
       identifier, not prose; it keeps its width and the panel scrolls, which is what `.tflens-panel`
       and `.tflens-panel-table` already have `overflow-x: auto` for. */
    .tflens-panel-table td .font-mono[b-ba2vancesw],
    .tflens-panel-table th .font-mono[b-ba2vancesw] {
        white-space: nowrap;
        overflow-wrap: normal;
    }

/* The fan-out band's key/value table has no header row of its own. */
.tflens-kv-panel td:first-child[b-ba2vancesw] {
    color: var(--muted-foreground);
}

/* REQ-UI-048 — the fan-out band's value cells hold figure PAIRS ("4.5 · 9" = median · max), so unlike
   every other numeric cell on this page they contain spaces and can break at one. At 390 the median
   and the max landed on separate lines, which reads as two unrelated numbers stacked in a cell rather
   than as the one figure it is. The pair stays whole and the panel scrolls, as it already does for the
   By-model column. Only visible once a phase actually HAS a spawn median — before the store was
   replayed this cell read "—" and nothing could wrap.

   The figure column is the MIDDLE of three (label · figure · note), so `:last-child` reaches the note
   and not the number. It is addressed by the class that means "this cell holds a figure". */
.tflens-kv-panel td.tflens-right[b-ba2vancesw] {
    white-space: nowrap;
}

/* REQ-UI-049 — the routing card is the narrower half of a 1.3fr / 1fr pair, so its first three
   columns are pinned and the "What it means" column keeps the rest. Without this the meaning wrapped
   to five lines and the row grew taller than the badge it explains. */
.tflens-routing-table[b-ba2vancesw]  th:nth-child(1),
.tflens-routing-table[b-ba2vancesw]  td:nth-child(1) {
    width: 4.5rem;
}

.tflens-routing-table[b-ba2vancesw]  th:nth-child(2),
.tflens-routing-table[b-ba2vancesw]  td:nth-child(2) {
    width: 1.5rem;
}

.tflens-routing-table[b-ba2vancesw]  th:nth-child(3),
.tflens-routing-table[b-ba2vancesw]  td:nth-child(3) {
    width: 5rem;
}

/* At 390 the three pinned columns leave the meaning column almost no width, and it wrapped one word
   per line — a 400px-tall row explaining a 22px badge. The floor makes the table wider than the
   viewport instead, so it scrolls inside DataTable's own `overflow-auto` wrapper the way every wide
   control on this page does (BRD-144). The floor is measured rather than picked: a cell's `width` is
   content-box and each carries 16px of padding a side, so 4.5 + 1.5 + 5 + 10 rem plus 4 x 32px of
   padding is 472px inside the 484px this card gives the table at 1280 — the meaning reads whole at
   the design's own width, and only the phone scrolls. */
.tflens-routing-table[b-ba2vancesw]  th:nth-child(4),
.tflens-routing-table[b-ba2vancesw]  td:nth-child(4) {
    min-width: 10rem;
}

.tflens-routing-table .tflens-bar[b-ba2vancesw] {
    width: 3.5rem;
}

/* REQ-UI-049 — the per-phase routing counts, closed by default so the card face stays the design's
   own three-row summary. */
.tflens-routing-byphase[b-ba2vancesw] {
    margin-top: 1rem;
}

.tflens-byphase[b-ba2vancesw] {
    margin-top: 0.5rem;
}

/* BRD-168 — the standing actor note under the last band. */
.tflens-actor-note[b-ba2vancesw] {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    min-width: 0;
}

/* The mockup underlines its in-copy links with a 3px offset. */
.tflens-link[b-ba2vancesw] {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.tflens-figure[b-ba2vancesw] {
    font-size: 1.25rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.3;
}

/* The time band's four figures — four across at 1280, two at 390 (`grid-cols-2 md:grid-cols-4` on
   the markup). The row gap is the only part no utility expresses as one class. */
.tflens-quad[b-ba2vancesw] {
    gap: 0.75rem 1rem;
    min-width: 0;
}
/* /Components/Pages/Export/CodeBlock.razor.rz.scp.css */
/*
    This block's own measurements. They stay as CSS rather than becoming utilities: `font-size:
    0.78rem`, `line-height: 1.6`, `max-height: 18rem` and the asymmetric bar padding are all arbitrary
    values, and TR-002's fix does NOT extend to those — the pre-built sheet can only carry a bracket
    class that existed when the bundle was built, so `text-[0.78rem]` / `max-h-[18rem]` are absent at
    runtime and would silently do nothing. The responsive variants TR-002 restored are not needed here:
    this component has no breakpoint of its own.
*/

.tflens-codeblock[b-ul6ktlnssp] {
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    overflow: hidden;
    background: var(--muted);
}

.tflens-codeblock-bar[b-ul6ktlnssp] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.25rem 0.25rem 0.25rem 0.875rem;
    border-bottom: 1px solid var(--border);
    background: var(--card);
}

/* The body is the only thing allowed to scroll — a long compare line must never widen the page. */
.tflens-codeblock-body[b-ul6ktlnssp] {
    margin: 0;
    padding: 0.75rem 0.875rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.78rem;
    line-height: 1.6;
    overflow-x: auto;
    white-space: pre;
    max-height: 18rem;
    overflow-y: auto;
}
/* /Components/Pages/Export/ExportSurface.razor.rz.scp.css */
/*
    The export surface's own measurements — the ones the shipped utility set still cannot express.

    TR-002 is closed: trblazeui.css ships the sm:/md:/lg:/xl:/2xl: variants now (40/48/64/80/96rem),
    so the two grids on this surface are `grid-cols-1 lg:grid-cols-3` / `lg:grid-cols-2` in the markup
    and the `@media (min-width: 64rem)` block that stood in for them is gone. What is left below is
    table-cell behaviour, which no utility reaches: every rule here targets a cell inside DataTable's
    own markup and therefore needs `::deep`.
*/

/* A long target path must wrap rather than widen the card.

   Changed 2026-09-01 (REQ-UI-032, BRD-144 clause 6): was `overflow-wrap: anywhere`, which broke
   `data/reports/2/2026-08-30/techieflow/snapshot.md` mid-segment because the run needed 307px in a
   262px card. `anywhere` breaks at the first character that will not fit; `break-word` breaks only a
   word that cannot fit a line on its own. The path now also carries `<wbr>` marks at its separators
   (`PathText`), so there is a real boundary to wrap on and the last-resort break stays a last resort. */
.tflens-break[b-gvafmr5wwo] {
    overflow-wrap: break-word;
    word-break: normal;
}

/*
    A script hash and a compare command are both longer than the column they sit in. `::deep` is needed
    because the cells belong to DataTable, not to this component's own markup, and a value that is
    clipped rather than wrapped reads as a shorter hash than it is.

    Same correction as `.tflens-break` above: `anywhere` split `.tfcore/telemetry/tf-metrics.sh`
    mid-token at 390 (211px of text in a 148px cell), which is the defect BRD-144 clause 6 names.
*/
.tflens-facts[b-gvafmr5wwo]  td {
    overflow-wrap: break-word;
    word-break: normal;
}

/* REQ-UI-032 — dataset identity. Every cell in this table is an identifier a reader must be able to
   re-type: a repo slug, a branch, a 7-character commit SHA, a sync age. `30e6616` needed 62px in a
   51px cell and came apart mid-hash — a broken SHA is a wrong SHA, on the one screen whose job is
   publishing a dataset someone else can reproduce. The cells are held whole and the table scrolls
   inside its own `overflow-x-auto` container instead, which is the treatment every other wide table
   on this page already gets. */
.tflens-shas[b-gvafmr5wwo]  td,
.tflens-shas[b-gvafmr5wwo]  th {
    white-space: nowrap;
}

/* REQ-UI-032 / REQ-UI-033 — mockup parity for the Export card (owner UAT, 2026-08-29). */

/* The mockup's card header is a two-part row: title + description on the left, the download icon
   button on the right, and this element is that row. */
.tflens-export-head[b-gvafmr5wwo] {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
}

/* REQ-UI-032 — the field column holds short, fixed labels (`Parity status`, `Script hash`). Holding
   them on one line is what the mockup does; the value beside them has `<wbr>` break points of its own
   to give ground with. No explicit width: `white-space: nowrap` already sizes the column to its
   longest label, and the 38% it used to claim was 22px MORE than `Parity status` needs — width the
   value column wanted, which is why `6 synced repo(s) · 1,174 records` came out over three lines at
   1280. (The `::deep thead { display: none }` that used to sit here is gone: the two facts tables
   pass `ShowHeader="false"` now — TR-012.) */
.tflens-facts[b-gvafmr5wwo]  td:first-child {
    white-space: nowrap;
    width: 1%;
    color: var(--muted-foreground);
}

/* REQ-UI-033 — the parity pill's two tones. Amber is the default for everything that is not an
   affirmative QUOTABLE.

   These reach the Badge through `::deep`, not as bare class selectors: `Class="tflens-parity-ok"` on
   a child component lands on an element that carries BADGE's scope attribute, never this file's, so a
   bare `.tflens-parity-ok { … }` compiles to a rule that can never match (REQ-NFR-021 — the same trap
   `.tflens-export-chip` above documents). The `.tflens-pill` wrapper is what gives `::deep` an
   ancestor inside this component to hang off. */
.tflens-pill[b-gvafmr5wwo]  .tflens-parity-ok {
    color: var(--alert-success);
    border-color: color-mix(in oklab, var(--alert-success) 45%, transparent);
    background: var(--alert-success-bg);
}

.tflens-pill[b-gvafmr5wwo]  .tflens-parity-warn {
    color: var(--alert-warning);
    border-color: color-mix(in oklab, var(--alert-warning) 45%, transparent);
    background: var(--alert-warning-bg);
}

/* REQ-UI-032 — the header's export chip. `docs/mockups/export.html` draws it as a blue `chart-1`
   chip; the app shipped TrBlazeUI's neutral Secondary button, which the parity gate reads as a
   different semantic colour bucket at both widths. Only the paint changes — it is still the same
   Button, with the same action and the same disabled state as the full-width one below it. */
.tflens-export-chip[b-gvafmr5wwo]  button {
    background: color-mix(in oklab, var(--chart-1) 15%, transparent);
    color: var(--chart-1);
    border-color: transparent;
}

/* REQ-UI-032 — the SHA column. `white-space: nowrap` stops the hash breaking, but the gate measures
   the CONTENT box: 83px of cell less 32px of DataTable padding left 51px against a 7-character hash
   that needs 62px, so the text was living in its own padding. A minimum width gives the column the
   room the figure actually needs, which is the honest reading of BRD-144 clause 6 — the cell must be
   wide enough for its longest unbreakable token, not merely prevented from breaking it. */
.tflens-shas[b-gvafmr5wwo]  td:nth-child(3),
.tflens-shas[b-gvafmr5wwo]  th:nth-child(3) {
    min-width: 7rem;
}
/* /Components/Pages/GateOutcomes.razor.rz.scp.css */
/* This file declares NO design tokens any more, and that is the whole of TR-001 landing here.

   The `--alert-*` five went when 2.1.0-ci.10 shipped them: trblazeui.css defines
   --alert-success/info/warning/danger with their -bg and -foreground companions in both token sets,
   so every Alert and every Success/Warning Badge on this page takes its colour from the library.

   The `--chart-*` five went when `wwwroot/app.css` was corrected to the mockup ramp (2026-09-12):
   its `:root --chart-2` is the mockup's green #34d399 now, not the violet that made this page's
   first-pass chip the wrong colour. The block that used to sit here was an exact duplicate of that,
   so keeping it would only have hidden the next divergence. The KPI chips, the clock chip and the
   Live / Backfilled share bars below all read `var(--chart-N)` and inherit it from :root.
   (REQ-UI-018.) */

/* REQ-UI-022 — the Late-gate coverage card's clock chip.

   `docs/mockups/gate-outcomes.html` draws it as `.chip.c1`: a 32px rounded tile tinted with `--chart-1`.
   The same two classes are declared in `StatTile.razor.css`, and that declaration cannot reach an element
   authored HERE — Blazor rewrites every rule in a scoped sheet to that component's own `b-…` attribute,
   so a rule written for one component's markup silently matches nothing in another's. Declaring them
   again in this file's scope is the fix, not a duplication to be tidied away later (REQ-NFR-021, which
   now fails the build on exactly this mistake). */
.tflens-stat-chip[b-3r4dhohusb] {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    flex: none;
    border-radius: calc(var(--radius, 0.625rem) - 2px);
}

.tflens-accent-1[b-3r4dhohusb] {
    background: color-mix(in oklab, var(--chart-1) 15%, transparent);
    color: var(--chart-1);
}

/* REQ-UI-018 — the mockup paints the live shares blue and the backfilled shares violet, which is what
   keeps the two provenances apart at a glance on a table that never pools them (SCHEMA.md §6). Both
   rendered in the default foreground, so the one distinction the row exists to draw was invisible. */
.tflens-bar-live[b-3r4dhohusb]  [data-slot="progress-indicator"],
.tflens-bar-live[b-3r4dhohusb]  > div > div {
    background: var(--chart-1);
}

.tflens-bar-backfilled[b-3r4dhohusb]  [data-slot="progress-indicator"],
.tflens-bar-backfilled[b-3r4dhohusb]  > div > div {
    background: var(--chart-5);
}

/* The one measurement on this page the shipped utility scale cannot express: 6.5rem is not a step on
   it, and an arbitrary `w-[6.5rem]` is not in the pre-built sheet (TR-002's fix restored the responsive
   variants and the base scale, not bracket values). The tab body's 1rem offset that used to live beside
   this rule is now the shipped `mt-4` utility in the markup. */
.tflens-share-bar[b-3r4dhohusb] {
    display: block;
    width: 6.5rem;
    flex: none;
}

.tflens-taint-trigger[b-3r4dhohusb]  button {
    justify-content: flex-start;
    text-align: left;
    height: auto;
    min-height: 2.25rem;
    white-space: normal;
}

/* The project-type tab strip is a single inline-flex row that grows with the number of types, so at
   a 390px viewport it measured 412px and dragged the whole page container sideways. It scrolls
   inside itself instead — the same treatment the wide tables already get. Scoped CSS does not cross
   into a child component's markup, hence ::deep to reach TabsList. */
[b-3r4dhohusb] [role="tablist"] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}
/* /Components/Pages/Harness.razor.rz.scp.css */
/* The Harness page's layout — only what nothing else supplies.

   Three things left this file on the 2.1.0-ci.10 uptake:

     * the `--chart-*` and `--alert-*` re-declarations. The shipped trblazeui.css defines all of them
       in its own `:where(:root)` / `:where(.dark)`, so the local copies overrode the theme for no
       reason. The chips and the bars read the shipped tokens now, which is also what makes a bar and
       its column's chip the same colour in light mode as in dark.
     * the three-harness grid. `grid gap-4 md:grid-cols-3` is written in Harness.razor now that the
       sheet ships the `md:` variant — a hand-written `@media (min-width: 48rem)` for a breakpoint the
       library already has is a rule that can drift from it.
     * the `.tflens-stack` copy. `wwwroot/app.css` declares that class once, globally (REQ-NFR-015).

   The one breakpoint that stays is the chart/dollars split below: `2fr / 1fr` is an arbitrary grid
   template, and arbitrary values are not in a pre-built utility sheet at any breakpoint. */

.tflens-harness-lower[b-37bqtrqbcs] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 1rem;
    min-width: 0;
}

@media (min-width: 64rem) {
    .tflens-harness-lower[b-37bqtrqbcs] {
        grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    }
}

/* REQ-UI-023, fixed 2026-08-30 — the card-header chips, which had silently stopped existing.

   `docs/mockups/harness.html` draws each harness icon inside a filled rounded chip — `.chip.c1` blue,
   `.c2` green, `.c3` pink — and this page asks for exactly that with `tflens-stat-chip tflens-accent-N`.
   Those classes are declared in `StatTile.razor.css`, and Blazor rewrites every rule in a scoped sheet
   to `…[b-<StatTile's id>]`. An element written HERE carries this page's scope attribute instead, so not
   one of those rules ever matched: the chip had no size, no ground and no colour, and the icon rendered
   as a bare monochrome glyph on the card. Scoped CSS is per-file, so the chip is declared again in this
   file's own scope.

   Colour comes from the same `--chart-N` tokens the bars are painted with — the shipped sheet's own,
   which the chart wrapper hands ApexCharts as `var(--chart-N)` — never from a literal, so a column's
   chip and its bar cannot drift apart. */
.tflens-stat-chip[b-37bqtrqbcs] {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    flex: none;
    border-radius: calc(var(--radius, 0.625rem) - 2px);
}

.tflens-accent-1[b-37bqtrqbcs] {
    background: color-mix(in oklab, var(--chart-1) 15%, transparent);
    color: var(--chart-1);
}

.tflens-accent-2[b-37bqtrqbcs] {
    background: color-mix(in oklab, var(--chart-2) 15%, transparent);
    color: var(--chart-2);
}

.tflens-accent-3[b-37bqtrqbcs] {
    background: color-mix(in oklab, var(--chart-3) 15%, transparent);
    color: var(--chart-3);
}

/* The key/value tables are headerless in the mockup: the left cell IS the label, so a header row would
   only repeat it. `DataTable ShowHeader="false"` renders no `thead` at all, so nothing is hidden here. */
.tflens-kv[b-37bqtrqbcs] {
    min-width: 0;
    /* REQ-UI-024 — the mockup puts this table in a `card-c flush` (zero padding), so the rows run edge
       to edge and their separators reach the card's borders. `CardContent` carries `p-6`, which inset
       the table by 24px a side and cost the value column 42px against the design measured at the same
       1280 viewport (142px here against 184px there) — the width that decides whether
       `insufficient data (n=0)` fits. The cell padding below restores the mockup's own 16px. */
    margin-inline: -1.5rem;
}

    .tflens-kv[b-37bqtrqbcs]  table {
        width: 100%;
    }

    /* The mockup's own cell padding (`.tbl td{padding:12px 16px}`), now that the table runs edge to
       edge. Vertical is kept at 8px: this table carries eleven rows to the mockup's ten and the card
       has to stay the same height as the two beside it. */
    .tflens-kv[b-37bqtrqbcs]  td {
        padding: 0.5rem 1rem;
        vertical-align: middle;
    }

    /* REQ-UI-023, fixed 2026-08-29 — the column split that made every figure on this page unreadable.

       Was: `td:first-child { width: 1%; white-space: nowrap }` + `td:last-child { overflow-wrap: anywhere }`.
       The intent ("the label shrinks so the value keeps the rest") inverted in practice: `width: 1%` with
       `nowrap` means the label claims whatever its LONGEST label needs — "Tokens per Verified REQ" on one
       unbroken line — and in a 266px card that left the value column **71px**. `overflow-wrap: anywhere`
       then did what it says and broke inside words, so `2,287,975,139` came out over three lines, split
       mid-number. A figure you cannot read you cannot quote, which is the one thing this product exists
       to do (BRD §1).

       Now: the label wraps on word boundaries and the value keeps the larger share, right-aligned as the
       mockup draws it. `break-word` replaces `anywhere` deliberately — it breaks a word only when that
       word cannot fit a line on its own, so a formatted number stays one token while a long text value
       ("fix-issues 31 · build-phase 29 · …") still wraps between its words.

       Widened 2026-08-30 (REQ-UI-023): 42% left the label cell 108px of content at 1280 — two pixels
       short of the 90px `Gate records` needs once the 8px + 12px cell padding is taken off — so that one
       label wrapped to two lines where the mockup holds it on one. It is now 45% with a 0.5rem right
       padding, which leaves ~100px of content: ten pixels of headroom on the widest single-line label,
       and the smallest widening that buys it, because every percent taken here is a percent the value
       column loses. `width` on a `td` is a hint rather than a floor in any case — the table algorithm
       will still grow a cell whose label cannot fit — so this sets the split, not a limit.

       CORRECTION 2026-09-01 (REQ-UI-023 / REQ-NFR-021): the 2026-08-30 note above was pasted AFTER the
       close marker that ended this comment, and before a second close marker that nothing had opened.
       CSS comments do not nest, so ten lines of English prose stood in the stylesheet as CSS, and the
       browser consumed them as the start of a selector — swallowing `.tflens-kv ::deep td:first-child`
       into a compound that matched nothing. The widening described above was therefore never applied in
       any browser, in any environment, for two days: no build error, no console warning, no failing test,
       and a checklist row that read `fixed`. The label column simply kept whatever the table algorithm
       gave it, which is the wrapping the note claims to have cured. The comment is balanced now, and
       `ScopedCssTests.EveryScopedStylesheetParses` fails the build on the next unbalanced one. (Which is
       why no comment in this repository writes a comment-close marker as prose — it ends the comment.)

       These are the mockup's own proportions: `.tbl.kv td:first-child{width:40%}` on a card whose cells
       carry 16px of padding a side against this table's 8px. */
    .tflens-kv[b-37bqtrqbcs]  td:first-child {
        /* 40%, which is `.tbl.kv td:first-child{width:40%}` — the mockup's own number — restored
           2026-09-01 now that this table runs edge to edge and carries the mockup's 16px cell
           padding. The 45% above was arithmetic done against the OLD 8px padding, and under the new
           one it left the value column 168px against the design's 184px: sixteen pixels, and the
           three that decided whether `insufficient data (n=0)` held one line at 1280. */
        width: 40%;
        overflow-wrap: break-word;
        word-break: normal;
    }

    /* Width only. The right alignment is the column's own `Align="DataTableColumnAlign.End"`. */
    .tflens-kv[b-37bqtrqbcs]  td:last-child {
        width: 60%;
        overflow-wrap: break-word;
        word-break: normal;
    }

/* REQ-UI-023 — the mockup's `sm` cell.

   `docs/mockups/harness.html` writes every value cell as `<td class="r">` and adds `sm` to exactly one
   of them: `Runs by cmd (top 3)`, whose value is a three-command list and the longest text on the page.
   `.sm{font-size:13px}` against the table's own `.tbl{font-size:14px}` is the design's own answer to
   that cell being long, and it is the reason the mockup draws the list on two lines rather than three.

   13px is an arbitrary value, and a pre-built utility sheet carries no arbitrary-value utilities, so
   this stays written out rather than becoming a `text-[13px]` that would not exist at runtime. */
.tflens-kv-small[b-37bqtrqbcs] {
    font-size: 0.8125rem;
    line-height: 1.25rem;
}

/* REQ-UI-023 — `Verdict mix` as the mockup draws it: a pass-share bar and one percentage, not a text
   dump of every verdict. The full breakdown is still reachable — it is the row's `title`. */
.tflens-verdict[b-37bqtrqbcs] {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    min-width: 0;
}

.tflens-verdict-track[b-37bqtrqbcs] {
    flex: 0 1 5.5rem;
    height: 0.5rem;
    border-radius: 999px;
    background: var(--muted, rgba(148, 163, 184, .25));
    overflow: hidden;
}

.tflens-verdict-fill[b-37bqtrqbcs] {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--alert-success);
}

.tflens-verdict-pct[b-37bqtrqbcs] {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    font-weight: 500;
}

/* The supplementary bar chart. `Height` is set on the chart itself; this only reserves the block.
   ApexCharts falls back to a fixed 400px canvas when it cannot resolve its container's width at init,
   which would widen the grid track past a phone viewport — hence the clip and the zero min-width. */
.tflens-chart[b-37bqtrqbcs] {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    margin-bottom: 0.5rem;
}

/* REQ-UI-025 — the one dollar figure on the page. */
.tflens-money[b-37bqtrqbcs] {
    font-size: 2.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

.tflens-money-empty[b-37bqtrqbcs] {
    font-size: 1rem;
    font-style: italic;
    color: var(--muted-foreground);
}

/* Separator is a child component, so its own root cannot carry this page's scope attribute; the rule's
   breathing space lives on the wrapper instead. */
.tflens-money-rule[b-37bqtrqbcs] {
    margin: 1rem 0;
}

.tflens-cost-heading[b-37bqtrqbcs] {
    min-width: 0;
}
/* /Components/Pages/Misses.razor.rz.scp.css */
/* Misses & rework — the few measurements TrBlazeUI's shipped utility set cannot express.

   2026-09-12 (TrBlazeUI 2.1.0-ci.10) — TR-001 is fixed: the shipped trblazeui.css now DEFINES
   --chart-1..5 and the whole --alert-* family in `:where(:root)` and `:where(.dark)`, and
   wwwroot/app.css overrides them once with the product's own OKLCH palette
   (docs/TfLens-UIDesign.md §Design system). The page-local copies that used to sit here were
   deleted: a page that re-declares the palette is exactly how one screen drifts from the next.
   The chips below therefore ride on the SEMANTIC --alert-* tokens rather than on a chart index,
   so amber still means "estimate / apportioned" and green still means "measured" whatever the
   chart ramp is set to. */
.tflens-stack[b-s2fwlvxw9p] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-width: 0;
}

.tflens-grid[b-s2fwlvxw9p] {
    display: grid;
    gap: 1rem;
    align-items: start;
    min-width: 0;
}

/* No `grid-template-columns` above, deliberately — a scoped class selector carries this file's scope
   attribute, so declaring one here would out-specify the `lg:grid-cols-*` utility on the div. The div
   carries `grid-cols-1` instead, which is the SAME `repeat(1, minmax(0, 1fr))`. The `minmax(0, …)` is
   load-bearing and must never become a bare `auto`: measured 2026-09-12, a plain `display:grid` sized
   its implicit track to max-content and the widest table pushed the page pane 787px sideways at 390. */

    .tflens-grid > *[b-s2fwlvxw9p] {
        min-width: 0;
    }

/* `lg:grid-cols-2` / `lg:grid-cols-3` are utilities in the markup now (TR-002). Only the origin
   pair stays here: its 1.35fr/1fr split is an arbitrary value, and arbitrary values are still not
   in the pre-built sheet. */
@media (min-width: 64rem) {
    /* The cross-tab is the wider of the pair: it carries a column per observed miss class. The
       minmax(0, …) is load-bearing — a bare `1.35fr` lets the table's intrinsic width push the column
       past its share and shove the card beside it off the right edge of the page. */
    .tflens-grid-origin[b-s2fwlvxw9p] {
        grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    }
}

/* The period select. TrBlazeUI's SelectTrigger sizes to its content, which on a phone lets the longest
   option ("All history (default)") push the header row wider than the viewport. */
.tflens-period[b-s2fwlvxw9p] {
    width: 12rem;
    max-width: 100%;
}

/* Any wide control — the cross-tab, a DataTable, the detail grid — scrolls inside its own box rather
   than widening the page. The visual gate treats a control inside such a box as reachable. */
.tflens-scroll-x[b-s2fwlvxw9p] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
}

/* REQ-UI-038 — the `miss-type` project-type tab strip is a single inline-flex row that grows with the
   number of types, and at 390 it measured 361px inside a 342px page container: the content was cut off
   rather than pushed out, which is exactly why the §4b visual gate (control boxes vs the viewport)
   passed it and only `mockup-parity`'s own scrollWidth check caught it. It scrolls inside itself
   instead — the identical treatment `GateOutcomes.razor.css` already gives the same strip. Scoped CSS
   does not cross into a child component's markup, hence ::deep to reach TabsList. */
[b-s2fwlvxw9p] [role="tablist"] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
    /* Re-verified 2026-09-11 at 390: TabsList ships `justify-center`, and a centred flex row that
       overflows spills BOTH ways — the first tab started 10px left of the strip, where no scroll can
       reach, so "app 91" lost its leading edge. Start-aligned, the overflow goes right only and the
       strip's own scroll reaches every tab. */
    justify-content: flex-start;
}

/* The plain cross-tab. A DataTableColumn needs a compile-time property per column and `miss_class` has
   no closed vocabulary, so the origin table's columns are built at render time from the observed
   classes. It carries the same numbers a chart would, which is also REQ-NFR-005's requirement. */
.tflens-table[b-s2fwlvxw9p] {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

    .tflens-table th[b-s2fwlvxw9p] {
        text-align: left;
        font-size: 0.75rem;
        font-weight: 500;
        color: var(--muted-foreground);
        padding: 0.625rem 0.75rem;
        border-bottom: 1px solid var(--border);
        white-space: nowrap;
    }

    .tflens-table td[b-s2fwlvxw9p] {
        padding: 0.625rem 0.75rem;
        border-bottom: 1px solid var(--border);
        vertical-align: middle;
    }

    .tflens-table tbody tr:last-child td[b-s2fwlvxw9p] {
        border-bottom: 0;
    }

    .tflens-table .tflens-num[b-s2fwlvxw9p] {
        text-align: right;
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

/* REQ-UI-037, 2026-08-30 — the origin-phase column names a TechieFlow task, and a task name is one
   token: `handoff-phase` has to read as `handoff-phase`, never as `handoff-` stacked above `phase`.
   A hyphen is a soft-wrap opportunity, so the browser was free to break the name in half, and this
   cross-tab is seven columns wide where the mockup's is five — measured on 2026-08-30 the first
   cell offered 88px of content box against the 101px the longest name needs, at BOTH 1280 and 390,
   and broke mid-name in both. Telling the column not to wrap makes it size to the name instead.
   The extra width costs the page nothing: the table already lives inside `.tflens-scroll-x`, which
   scrolls it inside its own card rather than widening the document. */
.tflens-table-origin th:first-child[b-s2fwlvxw9p],
.tflens-table-origin td:first-child[b-s2fwlvxw9p] {
    white-space: nowrap;
}

/* REQ-UI-053 — the first detail column carries the miss id AND the record's own one sentence, exactly as
   docs/mockups/misses.html draws it. The sentence is prose, so it must be allowed to wrap; the cell is
   given a floor and a ceiling so a long sentence neither squeezes the columns beside it to nothing nor
   stretches the table past the card it scrolls inside. */
.tflens-miss-cell[b-s2fwlvxw9p] {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 16rem;
    max-width: 26rem;
    white-space: normal;
}

    .tflens-miss-cell > div[b-s2fwlvxw9p] {
        line-height: 1.4;
        overflow-wrap: anywhere;
    }

    /* The miss id sits flush left above its sentence, as in the mockup. A stretched link Button centres
       its label in the column, which left the id floating 60px right of the sentence it names. */
    .tflens-miss-cell[b-s2fwlvxw9p]  button {
        align-self: flex-start;
        height: auto;
        padding-left: 0;
        padding-right: 0;
    }

/* REQ-UI-052 — the whose-gap answer is a short sentence in a `w-full` grid, so auto table layout
   compresses the column below its own text rather than letting the box scroll. Left alone, "It was
   written down and ignored anyway" stacked four words deep in a 90px cell. Giving the span a floor
   raises the column's min-content width instead, and the overflow lands on `.tflens-scroll-x` — which
   is what that box is for. (This is a column-width choice, not a library workaround: `DataTable` now
   takes `MinWidth`, but that widens EVERY column, and only this one needs a floor.) The span is
   authored here, so it carries this file's scope attribute; the `td` around it does not, which is why
   the rule goes on the span. */
.tflens-gap-cell[b-s2fwlvxw9p] {
    display: inline-block;
    min-width: 9.5rem;
    line-height: 1.4;
}

/* REQ-UI-054 — a review phase is one identifier, not prose, so it must never break mid-token. At 390px
   `day1-review` needed 86px in an 83px cell and wrapped after "day1-", which reads as two phases. The
   `w-full` table compresses a column below its own min-content width rather than letting its box
   scroll; holding the token on one line raises that width and the overflow lands on
   `.tflens-scroll-x`, which is what that box is for. This table is hand-authored, not a `DataTable`,
   so the library's new `MinWidth` does not reach it. */
.tflens-review-phase[b-s2fwlvxw9p] {
    display: inline-block;
    min-width: 6rem;
    white-space: nowrap;
}

/* The three cost headlines. Deliberately large, and deliberately never rendered as one number. */
.tflens-money[b-s2fwlvxw9p] {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.tflens-money-inline[b-s2fwlvxw9p] {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

/* BRD-123 / REQ-NFR-013 clause 5 — the rate-card card must not be mistakable for a measured tile at a
   glance. BRD-123's words are "never the same row, never the same styling"; docs/mockups/misses.html
   implements exactly that with `style="border-style:dashed"` over the default `.card`
   (`border:1px solid var(--border)`) — dashed, in the ordinary border grey, on a row of its own.
   Nobody was billed this.

   2026-08-30 — this rule carried the same defect as the measured ring below it, and for the same
   reason: `Card` renders its own root element, so that element never received this file's scope
   attribute and `.tflens-estimate { … }` compiled to a selector matching nothing. Measured in the
   browser before the fix, the card's computed `border-top-style` was `solid` — the estimate tile was
   styled identically to a measured one, which is precisely what BRD-123 forbids. Re-anchored on the
   page's own `.tflens-stack` (line 44, authored here, so it IS scoped) and reached through `::deep`.

   The amber border-colour and warning tint this rule used to declare are deliberately NOT restored.
   BRD-123 names no hue, the mockup keeps the default grey, and tinting the card would make the app's
   semantic colour read `amber` where the mockup reads `neutral` — the app inventing a meaning the
   design never gave it. Dashed-versus-solid is the whole of the distinction the mockup draws between
   an estimate and a measurement, and it is enough to carry it. */
.tflens-stack[b-s2fwlvxw9p]  .tflens-estimate {
    border-style: dashed;
}

/* REQ-UI-036, 2026-08-29 — the measured-dollars tile is ringed, as docs/mockups/misses.html draws it
   (`border-color:rgba(52,211,153,.45)`). It is the only MEASURED dollar figure in the product, and the
   estimate card directly below it shows a number an order of magnitude larger. Without the ring and the
   badge the two tiles look alike, which the mockup's own note calls out as handing the reader "a
   plausible wrong number".

   2026-08-30 — the ring was written but never painted. Blazor stamps the scope attribute only on
   elements authored in THIS file; `StatTile` renders its own `<Card>`, so the card element carries no
   `b-` attribute of ours and the bare `.tflens-measured { … }` rule compiled to a selector that matched
   nothing (verified in the browser: the tile's attributes contained no `b-…` at all, and its computed
   border-top-color was the plain `--border` grey). The rules are therefore anchored on `.tflens-kpi-row`,
   which IS ours, and reach the tile through `::deep` — the same shape the grid override above already
   uses. Colour comes from the app's own semantic-success token rather than a one-off hex, so this ring,
   the success badges in app.css and every success Alert stay one hue. */
.tflens-kpi-row[b-s2fwlvxw9p]  .tflens-measured {
    border-color: color-mix(in oklab, var(--alert-success) 55%, transparent);
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--alert-success) 35%, transparent);
}

/* 2026-09-11 (fix cycle 1) — the accent chips this page's OWN markup asks for: the amber triangle on the
   rate-card estimate card, and the green / amber / red chips on the three cost-of-rework cards.

   `docs/mockups/misses.html` draws each as `.chip.cN` — a 32px rounded tile tinted with its chart colour.
   The page wrote `tflens-stat-chip tflens-accent-N` on those spans, and those classes are declared only in
   StatTile.razor.css, whose rules Blazor rewrites to StatTile's own `b-…` scope attribute. A span authored
   HERE carries this file's attribute instead, so not one rule matched: the chip had no size, no ground and
   no colour, and the icon rendered as a bare white glyph — on the estimate card, the one tile whose amber
   is part of how a reader tells it apart from a measurement. Gate outcomes, Harness and Effort each hit
   the same trap and declare the chip again in their own scope; this is that fix, for this page. Colours
   come from the semantic `--alert-*` tokens, never from literals and never from a chart index — green
   is "measured", amber is "estimated / apportioned", red is "unattributable". */
.tflens-stat-chip[b-s2fwlvxw9p] {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    flex: none;
    border-radius: calc(var(--radius, 0.625rem) - 2px);
}

.tflens-accent-2[b-s2fwlvxw9p] {
    background: color-mix(in oklab, var(--alert-success) 15%, transparent);
    color: var(--alert-success);
}

.tflens-accent-4[b-s2fwlvxw9p] {
    background: color-mix(in oklab, var(--alert-warning) 15%, transparent);
    color: var(--alert-warning);
}

.tflens-accent-danger[b-s2fwlvxw9p] {
    background: color-mix(in oklab, var(--alert-danger) 15%, transparent);
    color: var(--alert-danger);
}

/* TR-033 / REQ-UI-053 — the per-miss filter sits in the card header, as docs/mockups/misses.html draws
   it (`.inwrap .input{width:220px;height:32px}`). It is bound to the grid through `@bind-SearchText`,
   so nothing about the filtering moved — only the box. On a phone it takes the full row under the
   title rather than squeezing the heading. */
.tflens-detail-filter[b-s2fwlvxw9p] {
    width: 100%;
}

@media (min-width: 40rem) {
    .tflens-detail-filter[b-s2fwlvxw9p] {
        width: 14rem;
    }
}
/* /Components/Pages/Prices.razor.rz.scp.css */
/* Price providers (REQ-UI-072) — what is left of docs/mockups/prices.html once TrBlazeUI 2.1.0-ci.10's
   own components and utilities have said everything they can say. Only two kinds of rule survive here:
   an override that reaches INSIDE a library component (a card's padding, the border the grid draws for
   itself) and a measurement the pre-built utility sheet has no class for. Layout, responsiveness,
   alignment, cell density and the badge tones are all markup now. NO WORKAROUND IS LEFT IN THIS FILE.

   Removed on the 2.1.0-ci.10 uptake (2026-09-12):
     TR-001  the local --alert-info-bg / --alert-warning-bg — the shipped sheet defines both, light and
             dark, so the standing note and the clash warning take their tint from the library.
     TR-002  the layout rules and most of the phone media query — sm:/md:/lg: variants ship, so the
             page's rows and grids are `md:` utilities in the markup.
     TR-025  the `::deep th, td` padding rule — `DataTable.Density="Compact"` sets the base and each
             column's own CellClass/HeaderClass, merged after it, carries the mockup's exact numbers.
     TR-029  the `white-space: nowrap` forced onto every claim pill — a Badge label no longer wraps by
             default, and `Wrap`/`Truncate` are there if one ever should.
     TR-031  the `th.text-right > div { justify-content: flex-end }` reach-in — each figure column
             carries `Align="DataTableColumnAlign.End"`, which moves th, header label box and cells.
     TR-032  `select { appearance: auto }` — the chevron class survives the class merge, so
             NativeSelect paints its own arrow. */

.tflens-prices[b-bo2rtrc0ej] {
    min-width: 0;
}

.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-source {
    overflow-wrap: anywhere;
}

    .tflens-prices[b-bo2rtrc0ej]  .tflens-prices-source:hover {
        text-decoration: underline;
    }

/* The rate table runs edge to edge in its card, as the mockup's flush card body does. */
.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-flush {
    padding: 0;
    min-width: 0;
}

    /* DataTable draws its own rounded border inside the card; flush, the card is the border. The box
       keeps its own horizontal scroll, which is where a narrow screen's overflow belongs. */
    .tflens-prices[b-bo2rtrc0ej]  .tflens-prices-flush .overflow-auto {
        border-width: 0;
        border-top-width: 1px;
        border-radius: 0;
    }

.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-nomatch {
    padding: 1rem 1.5rem;
}

/* 17.5rem is the mockup's 280px filter box; the pre-built sheet carries no max-width step for it. */
.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-filter {
    max-width: 17.5rem;
}

/* A card's footer: a rule above it, the unit and count on the left, the pager or the action on the
   right. Set here rather than as utilities because it overrides CardFooter's own padding. */
.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 0.75rem;
    padding: 1rem 1.5rem;
    border-top: 1px solid var(--border);
}

.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-foot-start {
    justify-content: flex-start;
}

.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-foot-end {
    justify-content: flex-end;
}

.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-hint {
    flex: 1 1 16rem;
    min-width: 0;
    font-size: 0.75rem;
    color: var(--muted-foreground);
}

/* The result of the last action is always in the page, and hidden until there is one. */
.tflens-prices-message[b-bo2rtrc0ej] {
    outline: none;
}

.tflens-prices[b-bo2rtrc0ej]  .tflens-prices-message-idle {
    display: none;
}

/* All that is left of the phone rules: the card paddings the mockup tightens on a phone. Each one
   reaches inside a Card, past where a `md:` utility on the caller's own element can go. The cut is
   the library's own `md` (48rem), not the mockup's hand-picked 700px, so the page has ONE
   breakpoint: the rows above stack on `md:` and the padding tightens with them. */
@media (max-width: 47.99rem) {
    .tflens-prices[b-bo2rtrc0ej]  .tflens-prices-foot {
        padding: 0.75rem 1rem;
    }

    .tflens-prices[b-bo2rtrc0ej]  .tflens-prices-cardhead {
        padding: 1rem;
    }

    .tflens-prices[b-bo2rtrc0ej]  .tflens-prices-cardbody {
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .tflens-prices[b-bo2rtrc0ej]  .tflens-prices-filter {
        max-width: none;
    }
}
/* /Components/Pages/Repos.razor.rz.scp.css */
/* The page's vertical rhythm. TrBlazeUI's shipped CSS carries `gap-4` but no `gap-6`, and the mockup's
   sections sit 24px apart, so the one measurement the page needs lives here. */

.tflens-stack[b-gf5ckms75x] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

/* Mobile overflow, measured honestly.
   The DataTable toolbar is a flex row whose search box carries `flex-1`. A flex item defaults to
   `min-width: auto`, so it refuses to shrink below its content: the row measured 413px inside a
   390px page container and dragged the whole page sideways. Note this was invisible to a
   `document.body.scrollWidth` check, because the shell's page container is itself `overflow:auto` —
   the body can never report the overflow. The honest measure is the page container's own
   scrollWidth against its clientWidth.
   `::deep` is required because the toolbar belongs to the DataTable component and carries no scope
   id of this page. */
.tflens-stack[b-gf5ckms75x]  [data-testid="repos-table"] {
    min-width: 0;
    max-width: 100%;
}

/* The blanket `* { min-width: 0 }` that used to live here was aimed at ONE thing: the DataTable
   toolbar's search box, which carries `flex-1` and so refused to shrink below its content, dragging
   the page sideways at 390. That toolbar is switched off now (ShowToolbar="false" — the filter moved
   to the card header to match the mockup), so the rule had no job left and was doing real harm: it
   let the Actions cell collapse until its second icon button was clipped by the card edge at 1440.
   Removed 2026-08-28. If a toolbar ever comes back, scope the rule to the toolbar, not to `*`. */

@media (max-width: 640px) {
    /* Let the toolbar wrap rather than overflow at phone width. */
    .tflens-stack[b-gf5ckms75x]  [data-testid="repos-table"] > div:first-child {
        flex-wrap: wrap;
        gap: 0.5rem;
    }
}

/* REQ-UI-011 — the Records column's right alignment and the Last-sync column's nowrap used to be
   two `nth-last-child` rules here, counted from the right so an inserted column would not shift
   them. Both are column parameters from TrBlazeUI 2.1.0-ci.10 (TR-031): `Align` and
   `CellClass`/`HeaderClass` on the DataTableColumn itself, in Repos.razor. Positional CSS that has
   to be re-counted whenever the grid gains a column is exactly what those parameters replace.

   Actions is still deliberately left as the library renders it. The mockup right-aligns that column
   too, and it was tried: pushing two icon buttons to the far edge of a grid that is a hair wider
   than its card at 1440 clipped the Remove button — a control a reader could see half of and not
   press. A pixel-accurate column that eats a destructive action is a worse outcome than a column
   that sits where the library puts it, so this stays a recorded, deliberate divergence from the
   mockup. `Align` does not change that: the clipping was a width problem, not an alignment API. */

/* The mockup's filter sits on the card-header row at 240px, not in the table's own toolbar. */
.tflens-repos-filter[b-gf5ckms75x] {
    width: 100%;
    max-width: 240px;
}

/* Nothing in this grid may be unreachable (REQ-UI-011, visual-truth gate §4b).

   Nine columns do not fit this card at 1440, and the DataTable was CLIPPING the surplus rather than
   scrolling it: the Remove button on every row was half-visible and unpressable, and no measurement
   caught it because the wrapper's scrollWidth equalled its clientWidth — the table had been squeezed
   to fit, so by its own arithmetic nothing overflowed. Only the screenshot showed it.

   `min-width: max-content` on the table is what makes it claim its natural width instead of being
   squeezed; the `overflow-x: auto` wrapper then has something real to scroll. This is the idiom the
   DevGuide prescribes for wide tables, and the visual gate treats a control inside such a box as
   reachable rather than off-viewport. */
.tflens-stack[b-gf5ckms75x]  [data-testid="repos-table"] {
    overflow-x: auto;
}

/* `min-width: max-content` was tried on the table above and is wrong: it pushes Actions off the
   visible area entirely, so the reader has to discover a horizontal scroll to reach Remove. Nine
   columns DO fit at 1440 once the cell padding is tightened, so the grid fits by default and the
   scroll box above is only the safety net at narrower widths.

   The tightening used to be a `::deep th, td { padding-left/right: 0.625rem }` rule here (TR-025).
   It is `Density="DataTableDensity.Compact"` on the DataTable now — the same 10px horizontally,
   from the component's own parameter rather than from a `::deep` reach into its internals. */

/* REQ-UI-011, 2026-08-29 — the mockup's filter carries a magnifier inside the field; the built one had
   no icon at all. Same data-URI technique as the auth fields: a CSS background cannot reference a
   Blazor component, so this is Lucide `search` inlined, stroked in the muted foreground. */
.tflens-repos-filter-icon[b-gf5ckms75x]  input {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="%23a1a1aa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>');
    background-repeat: no-repeat;
    background-position: 0.75rem center;
    background-size: 1rem 1rem;
    padding-left: 2.25rem;
}
/* /Components/Pages/Routing.razor.rz.scp.css */
/* Routing & economics — the few measurements TrBlazeUI's shipped utility set cannot express.

   TR-002 is CLOSED, in both halves:

   • trblazeui.css ships the sm:/md:/lg:/xl:/2xl: variants (40/48/64/80/96rem), so this page's own
     card grids are `grid-cols-1 lg:grid-cols-3` / `lg:grid-cols-5` in the markup and the media
     queries that stood in for them are gone.

   • The library's own 12-column helpers are responsive too, and the `.tflens-kpi-row` /
     `.tflens-kpi-3` / `.tflens-kpi-5` block that used to drive StatGroup's grid from here is gone
     with them. It was kept one pass too long on a bad reading: `.trblazeui-col-md-4` has the same
     DECLARATION as `.trblazeui-col-4` (`grid-column:span 4/span 4`), which looked like a
     non-responsive duplicate — but it sits inside `@media (min-width:48rem)`, and `-lg-` inside
     `@media (min-width:64rem)`. Identical declarations in different media scopes is how a responsive
     grid helper is built. The three-tile drift row now takes its column count from
     `Components/Shared/StatTile`'s own `trblazeui-col-12 / -md-4` spans, and measures 1 / 3 / 3
     across at 390 / 768 / 1280 with no rule from this file.

   The one rule below is NOT that workaround coming back. `docs/mockups/routing.html` draws the
   five-tile Poolable row as `repeat(5, 1fr)` at 1280 — measured in a browser, not read off the
   source — and a 12-column helper system cannot express five equal columns, because 5 does not
   divide 12. StatTile's widest span is `trblazeui-col-lg-3`, which is four across and leaves the
   fifth tile alone on a second row. So this is a design measurement the shipped scale has no step
   for, in the same class as the `--chart-*` palette above, and it stays until
   `Components/Shared/StatGroup` can carry a five-column row itself. Reported, not worked around. */
@media (min-width: 64rem) {
    .tflens-kpi-5[b-polsau3yw1]  .trblazeui-grid {
        grid-template-columns: repeat(5, minmax(0, 1fr));
    }

        .tflens-kpi-5[b-polsau3yw1]  .trblazeui-grid > * {
            grid-column: auto;
        }
}

/* This file declares NO design tokens any more (TR-001).

   The `--alert-*` five went when 2.1.0-ci.10 shipped them. The `--chart-*` five went when
   `wwwroot/app.css` was corrected to the mockup ramp (2026-09-12) — the block here was an exact
   duplicate of it. The KPI accent chips, `.tflens-money-saved` below and the totals chart's own
   series colours all read `var(--chart-N)` and inherit it from :root. */

/* The repricing headline. It is deliberately large, and it is deliberately never rendered without the
   estimate badge that sits directly beneath it in the markup. */
.tflens-money[b-polsau3yw1] {
    font-size: 2rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.tflens-money-saved[b-polsau3yw1] {
    color: var(--chart-2);
}

/* Four tab triggers are wider than a 390px viewport. Without this the page container scrolls sideways
   and drags the rest of the page off-screen with it; here the trigger row scrolls inside its own box. */
.tflens-tabs-scroll[b-polsau3yw1] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 2px;
}
/* /Components/Shared/CheckLine.razor.rz.scp.css */
/* The validation lines' pass/fail colours. TrBlazeUI's shipped CSS has no `text-alert-success`
   utility, so the two colours are taken straight from its own alert tokens here. */

.tflens-check-ok[b-12xth1488o] {
    display: inline-flex;
    color: var(--alert-success);
}

.tflens-check-bad[b-12xth1488o] {
    display: inline-flex;
    color: var(--destructive);
}
/* /Components/Shared/PathText.razor.rz.scp.css */
/* REQ-UI-032 / REQ-UI-033 — the wrap policy that goes with the break opportunities the markup offers.

   `break-word` breaks a word only when that word cannot fit a line on its own; `anywhere`, which this
   replaced elsewhere on the export screen, breaks at the first character that will not fit. With the
   `<wbr>` marks in place the browser has a legitimate boundary to take on every real path, so the
   last-resort break is now genuinely a last resort rather than the normal case. */
.tflens-path[b-1bw5ynzdaq] {
    overflow-wrap: break-word;
    word-break: normal;
}
/* /Components/Shared/Playbook/PlaybookEffortSurface.razor.rz.scp.css */
/* REQ-UI-050 — the measurements docs/mockups/effort-playbook.html needs that the shipped
   trblazeui.css does not carry: arbitrary values, and the ::deep rules that reach a child
   component's own root.

   Blazor scoped CSS is scoped to ONE component, so nothing declared in a page stylesheet can reach
   this component's markup — these are its own rules rather than references to them (REQ-NFR-021).

   No colour token is declared here. The shipped package stylesheet defines --chart-1..5 and the
   --alert-* family in :where(:root) / :where(.dark), and the app's own palette overrides them once
   in wwwroot/app.css; a copy on this root would only be a third place for them to drift. */

.pb-effort[b-hbnsboo7gb] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-width: 0;
}

/* The design's own h3: 16px, not the browser's 18.7px, and it carries no card of its own. */
.pb-h3[b-hbnsboo7gb] {
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.4;
    margin: 0;
}

.pb-section-head[b-hbnsboo7gb] {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

/* ------------------------------------------------------------------ filters */

.pb-filters[b-hbnsboo7gb] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.pb-filters-label[b-hbnsboo7gb] {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
    color: var(--muted-foreground, #a1a1aa);
    white-space: nowrap;
}

/* Twelve selects wrap onto as many rows as the viewport needs; at 390px each takes the full width
   rather than squeezing to an unreadable trigger. */
.pb-filter[b-hbnsboo7gb] {
    display: block;
    flex: 1 1 100%;
    min-width: 0;
}

@media (min-width: 40rem) {
    .pb-filter[b-hbnsboo7gb] {
        flex: 0 1 auto;
        min-width: 11rem;
    }
}

.pb-filters-spacer[b-hbnsboo7gb] {
    flex: 1 1 auto;
}

.pb-note[b-hbnsboo7gb] {
    margin-top: 0.75rem;
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--muted-foreground, #a1a1aa);
}

/* ------------------------------------------------------------------ figures */

/* A refusal is never rendered at headline weight — "unavailable" is a sentence, not a number. */
.pb-refused[b-hbnsboo7gb] {
    color: var(--muted-foreground, #a1a1aa);
    font-size: 0.9375rem;
    font-weight: 500;
}

.pb-lower-bound[b-hbnsboo7gb] {
    font-style: italic;
}

.pb-value-pair[b-hbnsboo7gb] {
    display: inline-flex;
    align-items: baseline;
    gap: 0.375rem;
    flex-wrap: wrap;
}

.pb-value-second[b-hbnsboo7gb] {
    font-size: 1rem;
    font-weight: 500;
    color: var(--muted-foreground, #a1a1aa);
}

/* BRD-160 — the measured tile is ringed and the estimate card is dashed, so the two can never be
   read as the same kind of dollar even at a glance.

   Both roots are rendered by StatTile and Card, which carry THEIR scope attributes and not this
   component's, so these reach them through ::deep rather than through a class that would compile to
   a selector matching nothing (REQ-NFR-021). */
.pb-effort[b-hbnsboo7gb]  [data-testid="pb-cost-measured"] {
    border-color: color-mix(in oklab, var(--chart-2) 45%, transparent);
}

.pb-effort[b-hbnsboo7gb]  [data-testid="pb-cost-estimate"] {
    border-style: dashed;
}

/* --------------------------------------------------------------------- bars */

.pb-bars[b-hbnsboo7gb] {
    display: flex;
    align-items: flex-end;
    justify-content: space-around;
    gap: 0.75rem;
    height: 190px;
    padding-top: 0.5rem;
    overflow-x: auto;
}

.pb-bar[b-hbnsboo7gb] {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: 0.375rem;
    height: 100%;
    min-width: 3.5rem;
}

.pb-bar-value[b-hbnsboo7gb] {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--muted-foreground, #a1a1aa);
    white-space: nowrap;
}

.pb-bar-stack[b-hbnsboo7gb] {
    display: flex;
    flex-direction: column-reverse;
    width: 100%;
    max-width: 3rem;
    min-height: 2px;
    border-radius: 4px;
    overflow: hidden;
}

.pb-bar-stack i[b-hbnsboo7gb] {
    display: block;
    width: 100%;
    min-height: 0;
}

.pb-bar-label[b-hbnsboo7gb] {
    font-size: 0.6875rem;
    color: var(--muted-foreground, #a1a1aa);
    text-align: center;
    word-break: break-word;
}

.pb-leg-1[b-hbnsboo7gb] { background: var(--chart-1); }
.pb-leg-2[b-hbnsboo7gb] { background: var(--chart-2); }
.pb-leg-3[b-hbnsboo7gb] { background: var(--chart-3); }
.pb-leg-4[b-hbnsboo7gb] { background: var(--chart-4); }
.pb-leg-5[b-hbnsboo7gb] { background: var(--chart-5); }

.pb-legend[b-hbnsboo7gb] {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.875rem;
    margin-top: 0.75rem;
    font-size: 0.6875rem;
    color: var(--muted-foreground, #a1a1aa);
}

.pb-legend-item[b-hbnsboo7gb] {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}

.pb-legend-item i[b-hbnsboo7gb] {
    display: block;
    width: 10px;
    height: 10px;
    border-radius: 3px;
}

/* ------------------------------------------------------------------- shares */

.pb-share[b-hbnsboo7gb] {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.pb-share-track[b-hbnsboo7gb] {
    display: block;
    flex: 1 1 auto;
    min-width: 3.5rem;
    max-width: 7rem;
    height: 6px;
    border-radius: 999px;
    background: color-mix(in oklab, currentColor 15%, transparent);
    overflow: hidden;
}

.pb-share-fill[b-hbnsboo7gb] {
    display: block;
    height: 100%;
    background: var(--chart-2);
}

.pb-share-turns[b-hbnsboo7gb] {
    background: var(--chart-3);
}

.pb-share-pct[b-hbnsboo7gb] {
    font-variant-numeric: tabular-nums;
    font-size: 0.8125rem;
    white-space: nowrap;
}

/* -------------------------------------------------------------------- grids */

/* One column on a phone, two from 64rem — `grid-cols-1 lg:grid-cols-2` on the markup. */
.pb-grid-2[b-hbnsboo7gb] {
    align-items: start;
    min-width: 0;
}

/* Wide tables scroll inside their own box so the page container never scrolls sideways at 390px. */
.pb-table-scroll[b-hbnsboo7gb] {
    overflow-x: auto;
    min-width: 0;
}

.tflens-stack[b-hbnsboo7gb] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-width: 0;
}

/* ------------------------------------------------------------------- detail */

.pb-detail[b-hbnsboo7gb] {
    margin-top: 1rem;
    border: 1px solid color-mix(in oklab, currentColor 12%, transparent);
    border-radius: 8px;
    overflow: hidden;
}

.pb-detail-trigger[b-hbnsboo7gb] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.625rem 0.875rem;
    font-size: 0.8125rem;
    background: color-mix(in oklab, currentColor 4%, transparent);
}

.pb-panels[b-hbnsboo7gb] {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1rem;
}

.pb-panel[b-hbnsboo7gb] {
    border: 1px solid color-mix(in oklab, currentColor 10%, transparent);
    border-radius: 8px;
    padding: 0.875rem;
    min-width: 0;
    overflow-x: auto;
}

.pb-panel-label[b-hbnsboo7gb] {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--muted-foreground, #a1a1aa);
    margin-bottom: 0.625rem;
}

.pb-chips[b-hbnsboo7gb] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    margin-bottom: 0.625rem;
}

.pb-kv[b-hbnsboo7gb],
.pb-tree[b-hbnsboo7gb] {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8125rem;
}

.pb-kv td[b-hbnsboo7gb],
.pb-tree td[b-hbnsboo7gb],
.pb-tree th[b-hbnsboo7gb] {
    padding: 0.375rem 0.5rem;
    border-bottom: 1px solid color-mix(in oklab, currentColor 8%, transparent);
    vertical-align: top;
    text-align: left;
}

.pb-tree th[b-hbnsboo7gb] {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--muted-foreground, #a1a1aa);
}

.pb-kv td:first-child[b-hbnsboo7gb] {
    width: 12rem;
    color: var(--muted-foreground, #a1a1aa);
    white-space: nowrap;
}

.pb-kv-value[b-hbnsboo7gb] {
    line-height: 1.5;
}

.pb-right[b-hbnsboo7gb] {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.pb-muted-row[b-hbnsboo7gb] {
    color: var(--muted-foreground, #a1a1aa);
}

.pb-small[b-hbnsboo7gb] {
    font-size: 0.8125rem;
}

/* A UTC instant is one fact and wraps to three lines in a narrow column if it is allowed to. */
.pb-when[b-hbnsboo7gb] {
    white-space: nowrap;
}

.pb-quality[b-hbnsboo7gb] {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
    min-width: 0;
}

.pb-quality-detail[b-hbnsboo7gb] {
    font-size: 0.6875rem;
    line-height: 1.4;
    color: var(--muted-foreground, #a1a1aa);
    word-break: break-word;
}

.pb-reasons[b-hbnsboo7gb] {
    margin: 0.625rem 0 0;
    padding-left: 1.125rem;
    font-size: 0.8125rem;
    line-height: 1.6;
    color: var(--muted-foreground, #a1a1aa);
}

/* The expander sits in a table cell, so the library's centred Button content is pinned left. The
   class rides on Button's own root, which carries Button's scope, so it is reached through ::deep. */
.pb-effort[b-hbnsboo7gb]  .pb-row-trigger {
    justify-content: flex-start;
    gap: 0.375rem;
    padding-left: 0;
    padding-right: 0.25rem;
}
/* /Components/Shared/Playbook/PlaybookHarnessSurface.razor.rz.scp.css */
/* The measurements this surface needs that nothing else supplies.

   The `.tflens-stack` copy that used to head this file is gone: `wwwroot/app.css` declares that class
   once, globally (REQ-NFR-015), so a per-component copy of it was only ever redundant.

   What is left is the chart/dollars split: `2fr / 1fr` is an arbitrary grid template, and a pre-built
   utility sheet carries no arbitrary values at any breakpoint, so the query stays hand-written.

   Blazor scoped CSS is scoped to ONE component, so the page-level rules in Harness.razor.css cannot
   reach this component's markup — these are its own copies rather than references to them. */

.pb-lower[b-bgpqhpvwfd] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 1rem;
    min-width: 0;
}

@media (min-width: 64rem) {
    .pb-lower[b-bgpqhpvwfd] {
        grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    }
}

.pb-money[b-bgpqhpvwfd] {
    font-size: 2.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

/* Separator is a child component, so its own root cannot carry this component's scope attribute; the
   rule's breathing space lives on the wrapper instead. */
.pb-money-rule[b-bgpqhpvwfd] {
    margin: 1rem 0;
}

.pb-cost-heading[b-bgpqhpvwfd] {
    min-width: 0;
}
/* /Components/Shared/Playbook/PlaybookMissesSurface.razor.rz.scp.css */
/* The Playbook axis of `/misses` — the measurements TrBlazeUI's shipped utility set cannot express.

   Blazor scoped CSS is scoped to ONE component, so the page-level rules in Misses.razor.css cannot
   reach this component's markup: these are its own copies rather than references to them (the same
   arrangement PlaybookHarnessSurface.razor.css already uses).

   2026-09-12 (TrBlazeUI 2.1.0-ci.10) — TR-001 and TR-002 are fixed. The shipped trblazeui.css now
   DEFINES --chart-1..5 and the whole --alert-* family, wwwroot/app.css overrides them once with the
   product's palette, and the `sm:`/`lg:` variants work, so the token block and the hand-written grid
   breakpoints that used to live here are gone. The chips below ride on the SEMANTIC --alert-* tokens
   rather than on a chart index, so green still means "measured" and amber "apportioned" whatever the
   chart ramp is set to. */
.pb-misses-surface[b-hiamjx8809] {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-width: 0;
}

.pb-grid[b-hiamjx8809] {
    display: grid;
    gap: 1rem;
    align-items: start;
    min-width: 0;
}

/* No `grid-template-columns` above, deliberately — a scoped class selector carries this file's scope
   attribute, so declaring one here would out-specify the `lg:grid-cols-*` utility on the div. The div
   carries `grid-cols-1` instead, which is the SAME `repeat(1, minmax(0, 1fr))`. The `minmax(0, …)` is
   load-bearing and must never become a bare `auto`: measured 2026-09-12, a plain `display:grid` sized
   its implicit track to max-content and the widest table pushed the page pane 787px sideways at 390. */

    .pb-grid > *[b-hiamjx8809] {
        min-width: 0;
    }

/* `lg:grid-cols-2` / `lg:grid-cols-3` are utilities in the markup now (TR-002). Only the origin
   pair stays here: its 1.35fr/1fr split is an arbitrary value, and arbitrary values are still not in
   the pre-built sheet. */
@media (min-width: 64rem) {
    /* The cross-tab is the wider of the pair: it carries a column per observed miss class. The
       minmax(0, …) is load-bearing — a bare `1.35fr` lets the table's intrinsic width push the column
       past its share and shove the card beside it off the right edge of the page. */
    .pb-grid-origin[b-hiamjx8809] {
        grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    }
}

/* Any wide control scrolls inside its own box rather than widening the page (BRD-144). */
.pb-scroll-x[b-hiamjx8809] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
}

/* The plain tables. A DataTableColumn needs a compile-time property per column, and neither
   `miss_class` nor the two found-axes have a closed vocabulary, so these columns are built at render
   time. They carry the same numbers a chart would, which is also REQ-NFR-005's requirement. */
.pb-table[b-hiamjx8809] {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

    .pb-table th[b-hiamjx8809] {
        text-align: left;
        font-size: 0.75rem;
        font-weight: 500;
        color: var(--muted-foreground);
        padding: 0.625rem 0.75rem;
        border-bottom: 1px solid var(--border);
        white-space: nowrap;
    }

    .pb-table td[b-hiamjx8809] {
        padding: 0.625rem 0.75rem;
        border-bottom: 1px solid var(--border);
        vertical-align: middle;
    }

    .pb-table tbody tr:last-child td[b-hiamjx8809] {
        border-bottom: 0;
    }

    .pb-table .pb-num[b-hiamjx8809] {
        text-align: right;
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    .pb-table .pb-bar[b-hiamjx8809] {
        width: 8rem;
    }

    /* A prose column squeezed to one word per line is unreadable. Giving it a floor makes the table
       overflow instead, which `.pb-scroll-x` turns into a scroll inside the card (BRD-144). */
    .pb-table .pb-note[b-hiamjx8809] {
        min-width: 13rem;
    }

@media (max-width: 30rem) {
    /* The share bar yields first: the count and the percentage beside it are the figures. */
    .pb-table .pb-bar[b-hiamjx8809] {
        width: 3.5rem;
    }
}

    /* A wire value is one token: `miss-fix` has to read as `miss-fix`, never as `miss-` stacked above
       `fix`, and `/implement` never as `/imple` + `ment`. A hyphen is a soft-wrap opportunity, so the
       browser is otherwise free to break the name in half. The extra width costs the page nothing —
       every table here lives inside `.pb-scroll-x`, which scrolls it inside its own card. */
    .pb-table td .font-mono[b-hiamjx8809],
    .pb-table th .font-mono[b-hiamjx8809] {
        white-space: nowrap;
    }

/* An origin phase names one command — `/implement` has to read as `/implement`, never broken in half.
   The extra width costs the page nothing: the table lives inside `.pb-scroll-x`. */
.pb-table-origin th:first-child[b-hiamjx8809],
.pb-table-origin td:first-child[b-hiamjx8809] {
    white-space: nowrap;
}

/* The three cost headlines. Deliberately large, and deliberately never rendered as one number. */
.pb-money[b-hiamjx8809] {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

/* Separator renders its own root, which cannot carry this component's scope attribute, so the rule's
   breathing space lives on the wrapper instead. */
.pb-rule[b-hiamjx8809] {
    margin: 1rem 0;
}

/* median / p90 on one tile: two figures, one unit each, never added together. */
.pb-pair[b-hiamjx8809] {
    display: inline-flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.25rem;
}

.pb-unit[b-hiamjx8809],
.pb-slash[b-hiamjx8809] {
    font-size: 1rem;
    font-weight: 500;
    color: var(--muted-foreground);
}

/* The accent chips the mockup puts on each cost card's header. */
.pb-chip[b-hiamjx8809] {
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    display: grid;
    place-items: center;
}

.pb-chip-2[b-hiamjx8809] {
    background: color-mix(in oklab, var(--alert-success) 15%, transparent);
    color: var(--alert-success);
}

.pb-chip-4[b-hiamjx8809] {
    background: color-mix(in oklab, var(--alert-warning) 15%, transparent);
    color: var(--alert-warning);
}

.pb-chip-5[b-hiamjx8809] {
    background: color-mix(in oklab, var(--chart-5) 15%, transparent);
    color: var(--chart-5);
}

.pb-chip-danger[b-hiamjx8809] {
    background: color-mix(in oklab, var(--alert-danger) 15%, transparent);
    color: var(--alert-danger);
}

/* docs/mockups/misses-playbook.html rings the measured-tokens tile and badges it `sole · complete`,
   because it is the ONE figure entitled to be called the cost of a repair and the apportioned column
   below it carries a number in the same unit. `StatTile` renders its own Card, so that element never
   receives this file's scope attribute — the rules are anchored on `.pb-kpi-row`, which IS ours, and
   reach the tile through `::deep`. Colour comes from the app's own semantic-success token so this
   ring, the success badges in app.css and every success Alert stay one hue. */
.pb-kpi-row[b-hiamjx8809]  .pb-measured {
    border-color: color-mix(in oklab, var(--alert-success) 55%, transparent);
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--alert-success) 35%, transparent);
}

/* TR-033 — the per-miss filter sits in the card header, as docs/mockups/misses-playbook.html draws it
   (`.inwrap .input{width:220px}`). It is bound to the grid through `@bind-SearchText`, so nothing
   about the filtering moved — only the box. */
.pb-detail-filter[b-hiamjx8809] {
    display: block;
    width: 100%;
}

@media (min-width: 40rem) {
    .pb-detail-filter[b-hiamjx8809] {
        width: 14rem;
    }
}
/* /Components/Shared/Playbook/PlaybookModelTokens.razor.rz.scp.css */
/* The bar chart's block. `Height` is set on the chart itself; this only reserves the box.

   ApexCharts falls back to a fixed 400px canvas when it cannot resolve its container's width at init,
   which would widen the grid track past a phone viewport — hence the clip and the zero min-width. This
   is the same guard Harness.razor.css puts round its own chart; scoped CSS is per-component, so it is
   declared again here rather than referenced. */

.pb-chart[b-n2mv96e1ya] {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}
/* /Components/Shared/Playbook/PlaybookQuestionsSurface.razor.rz.scp.css */
/* `.tflens-stack` and `.tflens-tab-body` are gone from this file: the page rhythm is declared once
   globally in `wwwroot/app.css` (a global rule reaches scoped markup; only the reverse is untrue), and
   the tab body's 1rem offset is now the shipped `mt-4` utility in the markup — TR-002 is closed and the
   base spacing scale ships in full. What is left is the one measurement no utility expresses. */

/* The phase_gate tab strip is a single inline-flex row that grows with the number of process gates, so
   at a 390px viewport it drags the whole page container sideways. It scrolls inside its own box
   instead — the same treatment the wide tables get. ::deep reaches TabsList, which is a child
   component and so cannot carry this component's scope attribute. */
.tflens-tabs-scroll[b-mvza2ykjjn]  [role="tablist"] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}
/* /Components/Shared/Playbook/PlaybookRoutingSurface.razor.rz.scp.css */
/* `.tflens-stack` and `.tflens-tab-body` are gone from this file: the page rhythm is declared once
   globally in `wwwroot/app.css` (a global rule reaches scoped markup; only the reverse is untrue), and
   the tab body's 1rem offset is now the shipped `mt-4` utility in the markup — TR-002 is closed and the
   base spacing scale ships in full. What is left is the one measurement no utility expresses. */

/* Three tab triggers are wider than a 390px viewport. Without this the page container scrolls sideways
   and drags the rest of the page off-screen with it; here the trigger row scrolls inside its own box.
   ::deep reaches TabsList, which is a child component and so cannot carry this component's scope
   attribute. */
.tflens-tabs-scroll[b-2khsin5gmm]  [role="tablist"] {
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}
/* /Components/Shared/RepoPicker.razor.rz.scp.css */
/* REQ-UI-077 — the repo picker in a report page's title row.

   TrBlazeUI's SelectTrigger sizes to its content, so a long owner/name would push the title row wider
   than a 390px viewport. The wrapper pins the width the period selects use and lets it shrink to the row,
   and the trigger's text is cut with an ellipsis rather than wrapping the row. */
.tflens-repo-picker[b-jgvqv03g8j] {
    width: 14rem;
    max-width: 100%;
    min-width: 0;
}

.tflens-repo-picker[b-jgvqv03g8j]  [data-testid="repo-picker"] {
    max-width: 100%;
    overflow: hidden;
}

.tflens-repo-picker[b-jgvqv03g8j]  [data-testid="repo-picker"] > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* /Components/Shared/ShellHeader.razor.rz.scp.css */
/* REQ-UI-010 — the header is ONE row at desktop, as every mockup draws it.

   The `flex-wrap` / `nowrap` switch on the HEADER itself is still written here: it is keyed to the
   header element, not to a component, so there is nothing to pass a class to. The BREADCRUMB's half
   of it has moved into the markup — see ShellHeader.razor and TR-027 below. */

.tflens-header-crumb[b-f5ymin90fb] {
    /* A flex item defaults to `min-width: auto`, which is what stopped the breadcrumb giving up any
       width and forced the right-hand cluster onto a second line. Zero lets it shrink to its wrapped
       size instead — the same two-line breadcrumb `docs/mockups/coverage.html` draws at 1280. */
    min-width: 0;
}

/* TR-027 is CLOSED. The `::deep ol { flex-wrap: wrap; row-gap: 0 }` rule that used to stand here is
   deleted: `Breadcrumb` takes a `Wrap` parameter now, and — the half that actually mattered — the
   shipped `<ol>` sets its row gap to `gap-y-0` on its own, so a wrapped trail no longer adds 10px of
   pure vertical padding and stands the header at 87px. The desktop `flex-nowrap` and the mockup's 4px
   column gap are expressed as `lg:` utilities on `Breadcrumb.ListClass` in ShellHeader.razor. */

.tflens-header-right[b-f5ymin90fb] {
    /* The right cluster states its own size and never absorbs the shortfall. */
    flex-shrink: 0;
}

/* REQ-UI-010 / BRD-144 — the Framework switch's segmented track.

   `docs/mockups/coverage.html` line 109 is the target and it is one box, not two:

       .fw{display:inline-flex;gap:2px;padding:3px;background:var(--muted);border-radius:8px}
       .fw .tab{height:28px;padding:0 10px;font-size:13px}
       .fw .tab .badge{height:18px;padding:0 6px;font-size:11px}

   TrBlazeUI paints its own track on `TabsList` (`h-10 rounded-md bg-muted p-1`), which is the element
   the mockup means. TR-024 is CLOSED: `TabsList` captures unmatched values and merges its `Class`, so
   `data-testid="framework-switch"` now sits on the track itself and the "move the ground UP onto the
   `Tabs` root and flatten `TabsList` to a bare row" workaround is deleted. What is left below re-sizes
   the library's own track instead of replacing it.

   These are geometry, not colour: the ground is the library's own `bg-muted`, and the active pill is
   its `data-[state=active]` styling — neither is painted here. They are written as CSS rather than as
   `Class` utilities because the pre-built bundle carries no arbitrary-value utilities, so `p-[3px]`,
   `h-[34px]`, `text-[13px]` and `gap-0.5` are all still absent at runtime and would silently do
   nothing. That part of TR-002 stands; the responsive variants below no longer do.

   `height: auto` is what makes the box 28 + 3 + 3 = 34px: the library ships `h-10` on the track, and a
   fixed height would also stop the 32px the `min-width: 64rem` block below asks for. 34px buys the
   header 6px of height and ~30px of width back against the nowrap budget described above. */

    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tablist"] {
        height: auto;
        gap: 2px;
        padding: 3px;
        border-radius: 8px;
    }

    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tab"] {
        height: 28px;
        padding: 0 10px;
        font-size: 13px;
        border-radius: 6px;
    }

    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tab"] [data-testid^="framework-count-"] {
        height: 18px;
        padding: 0 6px;
        font-size: 11px;
    }

/* REQ-UI-023, fixed 2026-08-30 — the crumb reads on ONE line at desktop.

   The 2026-08-29 fix above bought the header its single 64px row by letting the breadcrumb wrap
   internally, and that is what the owner then found: `Reports › Harness comparison` over two rows where
   `docs/mockups/harness.html` keeps it on one. The row's width budget at 1280 is exact — sidebar 256,
   header 1024, `px-4` 32 → 992 for trigger 20 + crumb + switch 233 + right cluster 524 + four 12px gaps
   — which left the crumb 167px against the 231px it wants.

   The 56px is taken back from spacing, not from content: nothing is hidden, shortened or truncated.
   The mockup's own header metrics are the target (`.hdr{gap:12px}` → 8px here, `.crumb{gap:6px}` where
   TrBlazeUI's list ships 10px, `.fw .tab{padding:0 10px}` → 8px), and the header's own inline padding
   drops from 16px to 10px. `flex: none` on the crumb then makes it the one item that states its width
   instead of absorbing the shortfall.

   The last-sync badge is the safety net, and it is needed: the longest crumb is not this page's but
   `Reports › Routing & economics`, and the badge's own text is data — `synced just now` through
   `synced 23 hours ago` — so the row's width is not a constant anyone can tune against. The right
   cluster may therefore shrink, but only the badge inside it can go below its content, and only after
   every other control has been given its full width. Worst case the badge ellipsises a character;
   the crumb stays whole and nothing leaves the viewport.

   Below 64rem none of this applies and the header keeps wrapping, which is the correct phone
   behaviour. */
@media (min-width: 64rem) {
    .tflens-header[b-f5ymin90fb] {
        flex-wrap: nowrap;
        gap: 0.5rem;
        padding-left: 0.625rem;
        padding-right: 0.625rem;
    }

    .tflens-header-crumb[b-f5ymin90fb] {
        flex: none;
    }

        .tflens-header-crumb[b-f5ymin90fb]  ol {
            /* `lg:flex-nowrap` and `lg:gap-x-1` do this half and are passed as utilities in the
               markup; there is no responsive `whitespace-*` variant in the pre-built sheet, so this
               one declaration stays here. */
            white-space: nowrap;
        }

    .tflens-header-right[b-f5ymin90fb] {
        gap: 0.375rem;
        flex-shrink: 1;
        min-width: 0;
    }

        /* The badge's own flex row, which would otherwise refuse to go below its content.
           Deliberately UNTAGGED: `Badge` renders a `<span>` in 2.1.0-ci.10, not a `<div>`, and a
           selector that names the tag of anything near a component is one upgrade away from silently
           not matching. `:has(> …)` selects the parent, which is SyncNowButton's own row — still a
           div today, and this rule no longer cares if it stops being one. */
        .tflens-header-right[b-f5ymin90fb]  :has(> [data-testid="last-sync-badge"]) {
            min-width: 0;
        }

        /* `Badge Truncate="true"` supplies the overflow, ellipsis and nowrap itself, and since
           TrBlazeUI 2.0.6 it keeps the Outline variant's `text-foreground` (TR-038), so the badge
           needs only this: `max-w-full` caps a flex item's width but does not let it shrink below
           its content, and no component parameter says that. */
        .tflens-header-right[b-f5ymin90fb]  [data-testid="last-sync-badge"] {
            min-width: 0;
        }

    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tablist"] {
        padding: 2px;
    }

    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tab"] {
        padding: 0 7px;
    }

        /* The tab's label/count pair ships `gap-1.5`; the mockup's own count sits closer. */
        .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tab"] > span {
            gap: 3px;
        }
}

@media (max-width: 63.999rem) {
    /* Wrapped layout: the spacer would otherwise claim a whole row of its own. */
    .tflens-header-spacer[b-f5ymin90fb] {
        display: none;
    }
}

/* ---------------------------------------------------------------------------------------------
   Fix cycle 1, 2026-09-11 — the header's controls drawn to docs/mockups/{misses,effort,prices}.html.

   The sidebar trigger is the mockup's 32px ghost icon button (`.btn.ghost.icon`); the library's
   SidebarTrigger ships `h-7 w-7`. The avatar's initials take the foreground ink at 600, as `.avatar`
   does; the library's fallback is muted ink at 500, which read as grey letters rather than a person.
   --------------------------------------------------------------------------------------------- */
.tflens-header[b-f5ymin90fb]  [data-sidebar="trigger"] {
    width: 2rem;
    height: 2rem;
}

.tflens-header[b-f5ymin90fb]  .tflens-avatar-initials {
    color: var(--foreground);
    font-weight: 600;
}

/* MISS-TfLens-20260911-38 (REQ-UI-007) — the phone header is ONE 64px row.

   It stood 139px tall at 390: breadcrumb on one line, the Framework switch on a second, Sync now and
   the last-sync badge on a third. The mockups keep the phone header to one row by shedding, in steps,
   exactly what they shed here — their own @media rules, copied breakpoint for breakpoint:

     ≤900px  .hdr{padding:0 12px;gap:8px}  .hdr>.badge.outline{display:none}  .fw .badge{display:none}
     ≤820px  .crumb{display:none}  .fw .tab{padding:0 9px;font-size:12px}  .hdr>.switch>svg{display:none}
     ≤700px  sync-now and user-menu drop their words (font-size:0) and keep the icon / the avatar
     ≤480px  .fw .tab{padding:0 7px;font-size:11px}

   Nothing is lost: the breadcrumb repeats the page's own H1, the sync time is on the Repos page and in
   the toast every sync raises, the repo counts are on Repos and the sidebar badge, and the theme
   switch still states its state through its label. The words on Sync now and the user name were
   already hidden below 768px by `md:flex`.

   Below 768px the sidebar has left the page (the menu button slides it out), so the header owns the
   full width and holds one row with the spacer pushing the right-hand cluster to the edge. From 768px
   to 1023px the sidebar is back beside it and the row keeps the wrap it had. */
@media (max-width: 56.25rem) {
    .tflens-header[b-f5ymin90fb] {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
        gap: 0.5rem;
    }

    .tflens-header-right[b-f5ymin90fb] {
        gap: 0.5rem;
    }

    .tflens-header[b-f5ymin90fb]  [data-testid="last-sync-badge"],
    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [data-testid^="framework-count-"] {
        display: none;
    }
}

@media (max-width: 51.25rem) {
    .tflens-header-crumb[b-f5ymin90fb] {
        display: none;
    }

    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tab"] {
        padding: 0 9px;
        font-size: 12px;
    }

    .tflens-header[b-f5ymin90fb]  .tflens-theme-toggle svg {
        display: none;
    }
}

@media (max-width: 47.999rem) {
    .tflens-header[b-f5ymin90fb] {
        flex-wrap: nowrap;
        height: 4rem;
        padding-top: 0;
        padding-bottom: 0;
    }

    .tflens-header-spacer[b-f5ymin90fb] {
        display: block;
        min-width: 0;
    }

    .tflens-header[b-f5ymin90fb]  .tflens-user-menu {
        gap: 0.25rem;
        padding-left: 0.25rem;
        padding-right: 0.25rem;
    }
}

@media (max-width: 30rem) {
    .tflens-header[b-f5ymin90fb]  .tflens-framework-switch [role="tab"] {
        padding: 0 7px;
        font-size: 11px;
    }
}
/* /Components/Shared/StatTile.razor.rz.scp.css */
/* The KPI tile's accent chip. Six of the seven rules below are `color-mix()` over a `--chart-*`
   token, which no utility can express at any Tailwind version, so the chip's geometry is kept here
   beside them rather than split between this file and the markup. (2.1.0-ci.10 does ship `size-8`
   and `rounded-md`, so that part is now a choice, not a gap.) */

.tflens-stat-chip[b-n13f0th1xt] {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    flex: none;
    border-radius: calc(var(--radius) - 2px);
}

.tflens-accent-1[b-n13f0th1xt] {
    background: color-mix(in oklab, var(--chart-1) 15%, transparent);
    color: var(--chart-1);
}

.tflens-accent-2[b-n13f0th1xt] {
    background: color-mix(in oklab, var(--chart-2) 15%, transparent);
    color: var(--chart-2);
}

.tflens-accent-3[b-n13f0th1xt] {
    background: color-mix(in oklab, var(--chart-3) 15%, transparent);
    color: var(--chart-3);
}

.tflens-accent-4[b-n13f0th1xt] {
    background: color-mix(in oklab, var(--chart-4) 15%, transparent);
    color: var(--chart-4);
}

.tflens-accent-5[b-n13f0th1xt] {
    background: color-mix(in oklab, var(--chart-5) 15%, transparent);
    color: var(--chart-5);
}

.tflens-accent-danger[b-n13f0th1xt] {
    background: color-mix(in oklab, var(--destructive) 15%, transparent);
    color: var(--destructive);
}

/* REQ-UI-018 — the KPI trend line the mockups show under every tile. Its colour is FIXED at
   `--chart-1`, not inherited from the tile's accent: UIDesign §Design system says "a small trend line
   uses --chart-1 colour" and every mockup strokes `var(--chart-1)` under a chip of any colour
   (`docs/mockups/gate-outcomes.html:61`). Taking the accent instead — which is what shipped, via
   `currentColor` under `@AccentClass` — painted the Failures-scored line amber off
   `StatAccent.Chart4` and was the app's only semantic-colour parity finding. The chip is what carries
   the tile's identity; the line is the same line on every tile, which is what makes a row of them
   comparable at a glance.

   The stroke sits on the POLYLINE, matching `.spark polyline { stroke: var(--chart-1) }` in the
   mockups. Routing it through `color: var(--chart-1)` on the svg draws the identical line, but it also
   makes the svg element itself report a semantic colour, and the parity gate reads the element rather
   than the mark inside it — so the app read `blue` against the mockup's inherited `neutral` and the
   finding survived a fix that had already worked. Same colour, same place in the cascade as the design. */
.tflens-spark[b-n13f0th1xt] {
    display: block;
    width: 100%;
    height: 24px;
    margin-top: 10px;
    overflow: visible;
    opacity: .85;
}

    .tflens-spark polyline[b-n13f0th1xt] {
        stroke: var(--chart-1);
    }

/* REQ-UI-036 — the title row now carries an optional provenance badge beside the metric name. */
.tflens-stat-title[b-n13f0th1xt] {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    min-width: 0;
}
