/* /Components/AppLogo.razor.rz.scp.css */
/* The wordmark's face, written once (D-301 row 47). Its size is the host's: the app bar sets 18px and the
   signed-out shell 25px, each through ::deep .wordmark. */
.wordmark[b-v79i4hoysv] {
    font-family: 'Inter', sans-serif;
    font-weight: 700;
    letter-spacing: -0.03em;
    color: var(--ink);
}
/* /Components/Binder/HoldingsGallery.razor.rz.scp.css */
.g-name[b-d2gjgmpbsa] { display: block; font-weight: 500; color: var(--ink); }
.g-sub[b-d2gjgmpbsa] { font-size: 12px; color: var(--mut2); }

/* The source-screen chip (D-284 slice 4 §3), the table's chip in the tile's geometry.
   Same mechanism as HoldingsTable's .h-scr, and for the same measured reason: the chip pairs a hue
   with a glyph AND the word, so the word is the one part the ellipsis may never eat, and
   ellipsising the whole line eats it — the word being last. So the line is a flex row, the NAME is
   its own ellipsis box, and the glyph and the state word are flex-shrink: 0.
   Tokens, from the shipped elements that mean the same thing rather than from the rework's hexes:
   --pos / --neg on the glyph and --mut2 on the state word are HoldingsTable.razor.css's own (`.h-chip-glyph`, `.h-chip-state`)
   (the Screener log's .al-glyph carries the same ▼ in --neg, ActivityLog.razor.css's .al-glyph.neg); the
   name takes --mut, which is the colour the table's chip name inherits from its row. Sizes: the
   glyph and state keep the table's 11 / 10.5px rather than the rework's gallery 10 / 9.5px —
   nothing in this app sets text below 10px (the EST badge is the floor) and a 9.5px state word is
   the part that may not be lost. The name takes the tile's own face at 11.5px, under .g-sub's
   12px, because the tile body is Inter where the table's cells are mono. */
.g-scr[b-d2gjgmpbsa] { display: flex; align-items: center; gap: 5px; margin-top: 4px; overflow: hidden; font-size: 11.5px; font-weight: 500; color: var(--mut); }
/* Above the tile link's stretched ::after (BinderGrid.razor.css), which covers the footer: without
   this the chip's own link is unreachable and every click on it opens the card instead of the
   screen. z-index 1 is .bg-tile-actions' layer, where the remove ✕ has sat since 2026-08-28. */
.g-scr a[b-d2gjgmpbsa] { display: inline-flex; align-items: center; gap: 5px; min-width: 0; color: inherit; text-decoration: none; position: relative; z-index: 1; }
.g-scr a:hover[b-d2gjgmpbsa] { text-decoration: underline; }
.g-chip-name[b-d2gjgmpbsa] { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.g-chip-glyph[b-d2gjgmpbsa] { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 11px; }
.g-chip-state[b-d2gjgmpbsa] { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; }
/* The table chip's pill, at the tile's smaller step (mockup :106 -- 1px 6px, same 4px radius). The tile footer is
   already Inter, so only the fill and the shape are new here. See HoldingsTable.razor.css for why the hue is --pos
   and --neg rather than the mockup's PAL value. */
.g-chip[b-d2gjgmpbsa] { padding: 1px 6px; border: 1px solid; border-radius: 4px; }
.g-chip.is-active[b-d2gjgmpbsa] { color: var(--pos); background: linear-gradient(var(--posBg), var(--posBg)), var(--card); border-color: color-mix(in srgb, var(--pos) 30%, transparent); }
.g-chip.is-exited[b-d2gjgmpbsa] { color: var(--neg); background: linear-gradient(var(--negBg), var(--negBg)), var(--card); border-color: color-mix(in srgb, var(--neg) 35%, transparent); }
.g-scr a.g-chip:hover[b-d2gjgmpbsa] { text-decoration: none; filter: brightness(0.95); }
/* The unlinked arm — a named screen the screen itself has nothing to say about — explains itself
   in a tooltip, so it reads as one (the table's .h-scr span[title] rule). */
.g-scr span[title][b-d2gjgmpbsa] { cursor: help; }

/* Everything in the footer whose whole job is to explain itself on hover, raised above the tile
   link's stretched ::after for the reason the chip's link is: hit-tested at the real tile width,
   each one landed on the tile link instead, which takes the pointer and fires no title at all —
   the EST badge's date (D-243 ruling 17), the unvalued dash and word, and the unknown chip's
   sentence. The selector is spans deliberately: this rule costs whatever it matches the card link,
   and the unvalued ROW carried the tip first, which put a dead band across the tile's full width —
   208.00 × 22.50px, 4.9% of a 230px tile, and the same bottom strip the ::after exists to win back.
   On the spans it is the dash (9.03 × 22.50px) and the word (55.39 × 17.25px): 1.2%, with the rest
   of the row a card link again. Measured at a 1480px viewport. */
.g-scr span[title][b-d2gjgmpbsa], .g-row span[title][b-d2gjgmpbsa] { position: relative; z-index: 1; }

.g-row[b-d2gjgmpbsa] { display: flex; justify-content: space-between; align-items: baseline; margin-top: 6px; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; }
.g-value[b-d2gjgmpbsa] { font-weight: 700; }
.g-pl.is-pos[b-d2gjgmpbsa] { color: var(--pos); }
.g-pl.is-neg[b-d2gjgmpbsa] { color: var(--neg); }
.is-unvalued[b-d2gjgmpbsa] { color: var(--mut); }
.g-unvalued[b-d2gjgmpbsa] { font-size: 11.5px; color: var(--mut2); }
.est[b-d2gjgmpbsa] { display: inline-block; margin-left: 6px; padding: 0 5px; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; color: var(--warnInk); background: var(--warnBg); border-radius: 3px; vertical-align: middle; cursor: help; }

/* Sits in BinderGrid's .bg-tile-actions overlay (top-right of the art), so it needs its own
   contrast against whatever the card art renders behind it -- a plain text button (the
   ledger's .l-edit dress) would vanish against light art. A small translucent chip instead;
   direction hue on hover only, as a destructive-action affordance, not an ambient state
   (docs/brand.md §4). */
.g-remove[b-d2gjgmpbsa] {
    padding: 2px 7px;
    background: rgba(15, 15, 12, 0.55);
    color: #FFFFFF;
    border: 0;
    border-radius: 999px;
    font-size: 10.5px;
    cursor: pointer;
}
.g-remove:hover[b-d2gjgmpbsa] { background: var(--neg); }
/* /Components/Binder/HoldingsTable.razor.rz.scp.css */
/* Owner ruling, 2026-08-27 (D-195, fifth UAT finding): the two Binder tables mirror the
   Browse/Set roster. Every rule below takes its declarations from RosterTable.razor.css
   -- and the table face from app.css's `--table-*` (D-298 UAT walk 2026-10-08, finding 2) --
   .roster-table (container), .rt-head/.rt-row (row box), .rt-head (sticky header type),
   .rt-head-cell/.rt-grip (grip), .rt-cell/.rt-cell-name (cell type). Only what the roster
   has no equivalent of is decided here: the totals row, the ± direction hues, EST. The
   roster's cells all centre, its name cell included, so Card centres here too. */

/* .roster-table: no container padding -- the rows carry their own 16px gutters. The X axis
   clips only; the Y axis stays visible so the page is the one scroller and the sticky
   header works (the roster's D-118 lesson). */
.holdings[b-ykezn3sok7] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    overflow-x: clip;
}

/* .rt-head, .rt-row: one box for every row kind, no column gap. */
.h-head[b-ykezn3sok7], .h-row[b-ykezn3sok7], .h-tot[b-ykezn3sok7] { display: grid; align-items: center; padding: 6px 16px; }

/* .rt-head: pinned below the 48px nav (z-index 10 under the nav's 20), solid --card so rows
   passing beneath are masked. The head's type is inherited by its cells; nothing per-column
   below is allowed to reach a head cell (every per-column rule is scoped to .h-row/.h-tot),
   which is the defect D-195's third finding fixed. */
.h-head[b-ykezn3sok7] {
    position: sticky;
    top: 48px;
    z-index: 10;
    background: var(--card);
    border-bottom: 1px solid var(--line);
    border-radius: 9px 9px 0 0;      /* D-298 UAT walk 2026-10-08, finding 5: .rt-head's corners, the same defect in this copy */
    font: var(--table-head-font);
    letter-spacing: var(--table-head-tracking);
    text-transform: uppercase;
    color: var(--mut2);
}

/* .rt-head-cell / .rt-head-cell.sortable(:hover): the sort control is a button here rather
   than the whole cell, so the reset lets it inherit the head's type and the hover is its --
   its tracking and case too, which a button does not inherit by default (the browser's own
   button style sets letter-spacing: normal and text-transform: none; BacktestEntries.razor.css's
   .bt-sort, for the same reason). */
.h-head > div[b-ykezn3sok7] { position: relative; text-align: center; user-select: none; }
.h-sort[b-ykezn3sok7] { background: none; border: 0; padding: 0; color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; }
.h-sort:hover[b-ykezn3sok7] { color: var(--ink); }

/* .rt-grip */
.h-grip[b-ykezn3sok7] {
    position: absolute;
    right: 0;
    top: 0;
    cursor: col-resize;
    color: var(--line3);
    touch-action: none;
}

/* .rt-row */
.h-row[b-ykezn3sok7] { border-bottom: 1px solid var(--line4); }

/* .rt-cell / .rt-cell-name. min-width: 0 lets the ellipsis work inside a grid track. */
.h-row > div[b-ykezn3sok7], .h-tot > div[b-ykezn3sok7] { min-width: 0; text-align: center; font: var(--table-cell-font); color: var(--mut); }
.h-row .h-card[b-ykezn3sok7], .h-tot .h-card[b-ykezn3sok7] { font: 500 14px 'Inter Tight', sans-serif; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The Source screen link joins these rather than taking a copy of the global .row-link
   (app.css's .row-link, whose own comment records three duplicates already collapsed into it): the
   underline hover is this page's convention for a name-in-a-cell, shipped on the ledger's
   Source screen column two days earlier (TransactionsLedger.razor.css's .l-screen a / .l-screen a:hover). */
.h-card a[b-ykezn3sok7], .h-scr a[b-ykezn3sok7] { color: inherit; text-decoration: none; }
.h-card a:hover[b-ykezn3sok7], .h-scr a:hover[b-ykezn3sok7] { text-decoration: underline; }

/* No roster equivalent: the totals row is a row with the head's separator weight, at 600. */
.h-tot[b-ykezn3sok7] { border-top: 1px solid var(--line); }
.h-tot > div[b-ykezn3sok7], .h-tot .h-card[b-ykezn3sok7] { font-weight: 600; }

/* Direction hues follow the number (docs/brand.md §4); the fills are never used as text. */
.h-pl.is-pos[b-ykezn3sok7] { color: var(--pos); }
.h-pl.is-neg[b-ykezn3sok7] { color: var(--neg); }

.is-unvalued[b-ykezn3sok7] { cursor: help; }

/* The source-screen chip (D-284 slice 4 §3). The chip pairs a hue with a glyph AND a word
   (brand.md §4): colour alone never says ACTIVE -- so the word is the one part of the cell the
   ellipsis may never eat, and ellipsising the cell is exactly what eats it, the word being last.
   Measured in headless Chrome at the shipped 170px track, in the face below: the whole chip
   wants 196.95px, so `● Quiet Accumulation ACTIVE` rendered `● Quiet Accumulation …` with the
   state gone entirely, on any name past ~14 characters. The rework's own mechanism instead
   (Cardstock Binder.rework-2026-09-15.html:128): a flex row, the NAME its own ellipsis box,
   flex-shrink: 0 on the two fixed parts -- `● Quiet Accumul… ACTIVE` at the same width.
   The ledger's .l-screen (TransactionsLedger.razor.css's .l-row .l-screen) ellipsises its whole cell safely
   only because that cell holds a name and nothing else.
   The link takes the cell's own --mut so the name reads as data rather than as a second accent
   in every row, and the hue sits on the glyph -- --pos and --neg, the direction pair every other
   surface uses as text (the Screener log's .al-glyph is the same ▼ in the same token,
   ActivityLog.razor.css's .al-glyph.pos / .al-glyph.neg). Scoped to .h-row like .h-card above: the head cell and the
   empty totals cell are not chips, and a bare per-column rule reaching them is the defect
   D-195's third finding fixed. */
.h-row .h-scr[b-ykezn3sok7] { display: flex; justify-content: center; align-items: center; gap: 6px; overflow: hidden; }
.h-row .h-scr a[b-ykezn3sok7] { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.h-chip-name[b-ykezn3sok7] { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.h-chip-glyph[b-ykezn3sok7] { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 11px; }
.h-chip-state[b-ykezn3sok7] { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; }
/* UAT 2026-09-19, owner: "the enter and exit chips should have some background coloring to them so they look like
   an actual chip." This was the app's only bare-text chip -- the ledger's own .chip.buy/.sell and the Screener's
   .rc-chip.is-added/.is-removed are all filled pills -- and the mockup this cell cites draws it filled too
   (Cardstock Binder.rework-2026-09-15.html:128: background, 1px border, 4px radius, padding, name in Inter). The
   build had kept the glyph hue and dropped the rest.
   The hue is --pos/--neg, NOT the mockup's PAL.neg3: tokens.css owns colour where the prototypes no longer do
   (CLAUDE.md), ContrastTests:302-311 already pins both on their own tint over --card in all four modes, and
   .rc-chip.is-removed -- the shipped element that means the same thing -- uses --neg. The tint lays over an opaque
   --card layer of its own so a hovered row's --hov cannot move the pinned ground (TintOverCardCssTests).
   Inter repays the padding and then some: the name holds 14 characters here where the bare mono chip held 13. */
.h-chip[b-ykezn3sok7] { font-family: 'Inter', system-ui, sans-serif; font-size: 12.5px; font-weight: 500; padding: 2px 7px; border: 1px solid; border-radius: 4px; }
.h-chip.is-active[b-ykezn3sok7] { color: var(--pos); background: linear-gradient(var(--posBg), var(--posBg)), var(--card); border-color: color-mix(in srgb, var(--pos) 30%, transparent); }
.h-chip.is-exited[b-ykezn3sok7] { color: var(--neg); background: linear-gradient(var(--negBg), var(--negBg)), var(--card); border-color: color-mix(in srgb, var(--neg) 35%, transparent); }
/* A pill underlines badly, so the chip takes the mockup's hover instead of .h-scr a:hover's underline. */
.h-scr a.h-chip:hover[b-ykezn3sok7] { text-decoration: none; filter: brightness(0.95); }
/* The two unlinked arms -- a named screen with nothing to say, and a lot naming none -- explain
   themselves in a tooltip, so both take the help cursor from carrying one. */
.h-row .h-scr span[title][b-ykezn3sok7] { cursor: help; }

/* --neg2 is a fill hue, never text (ContrastTests): it is the 3px edge and nothing else.
   Drawn as an inset shadow rather than the rework's border-left (:125), so the edge costs the
   row no layout at all and an unexited row needs no transparent placeholder to stay aligned. */
.h-row.is-signal[b-ykezn3sok7] { box-shadow: inset 3px 0 0 var(--neg2); }

/* The footer count explains the grain it counts (BinderCopy.PositionsTip). */
.h-tot .h-card[b-ykezn3sok7] { cursor: help; }

/* Same dress as the ledger's .l-edit button (TransactionsLedger.razor.css); direction hue on
   hover only, as a destructive-action affordance, not an ambient state (docs/brand.md §4). */
.h-remove button[b-ykezn3sok7] { background: none; border: 0; color: var(--mut2); font-size: 11px; cursor: pointer; }
.h-remove button:hover[b-ykezn3sok7] { color: var(--neg); }
.est[b-ykezn3sok7] { display: inline-block; margin-left: 6px; padding: 0 5px; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; color: var(--warnInk); background: var(--warnBg); border-radius: 3px; vertical-align: middle; cursor: help; }
/* /Components/Binder/PerformanceChart.razor.rz.scp.css */
.pfc-plotrow[b-fgdr2f7f0d] { display: flex; gap: 8px; }
.pfc-gut[b-fgdr2f7f0d] { position: relative; width: 44px; flex-shrink: 0; }
.pfc-gut span[b-fgdr2f7f0d] { position: absolute; right: 0; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 10.5px; color: var(--mut2); }
.pfc-ymax[b-fgdr2f7f0d] { top: 2px; }
.pfc-ymin[b-fgdr2f7f0d] { bottom: 2px; }
.pfc-plot[b-fgdr2f7f0d] { position: relative; flex: 1; min-width: 0; height: 230px; cursor: crosshair; }
.pfc-plot svg[b-fgdr2f7f0d] { display: block; width: 100%; height: 230px; }
.pfc-mid[b-fgdr2f7f0d] { stroke: var(--line4); }
.pfc-index[b-fgdr2f7f0d] { stroke: var(--mut2); stroke-width: 1.5; }
.pfc-binder[b-fgdr2f7f0d] { stroke: var(--acc); stroke-width: 2; }
.pfc-dashed[b-fgdr2f7f0d] { stroke-dasharray: 4 4; }
.pfc-dotted[b-fgdr2f7f0d] { stroke-linecap: round; stroke-dasharray: 0.1 3.5; }
/* One hollow point per chart, and it is always the live point (D-289 ruling 4): today's value by the
   tile rule, reached by the dashed segment from the last stored night; tonight's pass stores it. */
.pfc-hollow[b-fgdr2f7f0d] { position: absolute; width: 8px; height: 8px; border-radius: 50%; background: var(--card); border: 1.5px solid var(--acc); transform: translate(-50%, -50%); cursor: help; z-index: 3; box-sizing: border-box; }
.pfc-hollow:focus-visible[b-fgdr2f7f0d] { outline: 2px solid var(--acc); outline-offset: 1px; }
/* Direct labels at each line's end, in that line's stroke (D-198 walk item 8): the Binder label sits
   above its hollow point, right-aligned to the plot edge, or below it when the point is within the top
   12% of the plot; the index label sits just right of the ragged end, where the index line stops at the
   last stored night, in the gap before the live point. */
.pfc-lab[b-fgdr2f7f0d] { position: absolute; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 10.5px; white-space: nowrap; pointer-events: none; z-index: 2; }
.pfc-lab-binder[b-fgdr2f7f0d] { color: var(--acc); font-weight: 700; }
.pfc-lab-binder.pfc-lab-above[b-fgdr2f7f0d] { transform: translate(-100%, -150%); }
.pfc-lab-binder.pfc-lab-below[b-fgdr2f7f0d] { transform: translate(-100%, 60%); }
.pfc-lab-index[b-fgdr2f7f0d] { color: var(--mut2); transform: translate(8px, -50%); }
.pfc-empty[b-fgdr2f7f0d] { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 12.5px; color: var(--mut2); text-align: center; padding: 0 40px; pointer-events: none; }
.pfc-hits[b-fgdr2f7f0d] { position: absolute; inset: 0; display: flex; }
.pfc-hit[b-fgdr2f7f0d] { flex: 1; }
.pfc-vline[b-fgdr2f7f0d] { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line3); pointer-events: none; transform: translateX(-50%); }
.pfc-tip[b-fgdr2f7f0d] { position: absolute; top: 8px; left: 8px; background: var(--tooltipBg); border: 1px solid var(--line); border-radius: 6px; padding: 5px 9px; pointer-events: none; z-index: 4; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; }
.pfc-tip-m[b-fgdr2f7f0d] { font-size: 11px; color: var(--mut2); }
.pfc-tip-b[b-fgdr2f7f0d] { font-size: 12.5px; font-weight: 700; color: var(--acc); }
.pfc-tip-i[b-fgdr2f7f0d] { font-size: 12.5px; color: var(--mut); }
.pfc-tip-s[b-fgdr2f7f0d] { font-size: 11px; color: var(--mut2); margin-top: 3px; border-top: 1px solid var(--line4); padding-top: 3px; display: flex; align-items: center; }
.pfc-tip-w[b-fgdr2f7f0d] { font-size: 11px; color: var(--mut); }
.pfc-xax[b-fgdr2f7f0d] { position: relative; height: 16px; margin-top: 4px; margin-left: 52px; }
.pfc-x[b-fgdr2f7f0d] { position: absolute; top: 0; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 10.5px; color: var(--mut2); white-space: nowrap; }
.pfc-x-start[b-fgdr2f7f0d] { transform: none; }
.pfc-x-middle[b-fgdr2f7f0d] { transform: translateX(-50%); }
.pfc-x-end[b-fgdr2f7f0d] { transform: translateX(-100%); }
/* /Components/Binder/PerformanceTab.razor.rz.scp.css */
.pf[b-fi456nyy6q] { display: flex; flex-direction: column; gap: 12px; }
.pf-panel[b-fi456nyy6q] { border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; background: var(--card); }
.pf-head[b-fi456nyy6q] { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; font-size: 12.5px; color: var(--mut2); margin-bottom: 8px; }
.pf-title[b-fi456nyy6q] { font-family: 'Inter Tight', sans-serif; font-weight: 600; font-size: 17px; color: var(--ink); }
.pf-lg[b-fi456nyy6q] { display: inline-flex; align-items: center; gap: 5px; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; color: var(--mut); white-space: nowrap; }
.pf-sw[b-fi456nyy6q] { display: inline-block; width: 14px; height: 2.5px; border-radius: 1px; }
.pf-sw-binder[b-fi456nyy6q] { background: var(--acc); }
.pf-sw-index[b-fi456nyy6q] { background: var(--mut2); }
.pf-key[b-fi456nyy6q] { stroke: var(--acc); stroke-width: 2; }
.pf-key-dashed[b-fi456nyy6q] { stroke-dasharray: 4 4; }
.pf-key-dotted[b-fi456nyy6q] { stroke-linecap: round; stroke-dasharray: 0.1 3.5; }
.pf-caption[b-fi456nyy6q] { cursor: help; border-bottom: 1px dotted var(--mut3); white-space: nowrap; }
.pf-spacer[b-fi456nyy6q] { flex: 1; }
.pf-range[b-fi456nyy6q] { display: inline-flex; gap: 2px; border: 1px solid var(--line); border-radius: 6px; padding: 2px; background: var(--bg); }
.pf-range button[b-fi456nyy6q] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 11.5px; padding: 2px 8px; border: 0; border-radius: 4px; background: transparent; color: var(--mut); cursor: pointer; }
.pf-range button[aria-pressed="true"][b-fi456nyy6q] { background: var(--card); color: var(--ink); font-weight: 700; box-shadow: 0 0 0 1px var(--line); }
.pf-range button:disabled[b-fi456nyy6q] { color: var(--mut); cursor: not-allowed; }
.pf-edge[b-fi456nyy6q] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 13px; font-weight: 600; white-space: nowrap; }
.pf-edge.pos[b-fi456nyy6q] { color: var(--pos); }
.pf-edge.neg[b-fi456nyy6q] { color: var(--neg); }
/* A dash-plus-gate slot is a centred flex row: PendingGlyph is a 12px label glyph, and on
   the baseline of a 13–25.5px value it sits 1–5px below the dash's centre (D-198 UAT finding 2). */
.pf-edge.na[b-fi456nyy6q] { color: var(--mut2); font-weight: 400; display: inline-flex; align-items: center; }
.pf-edge small[b-fi456nyy6q] { font-weight: 400; color: var(--mut2); font-size: 11.5px; margin-left: 6px; }
.pf-tiles[b-fi456nyy6q] { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.pf-tile[b-fi456nyy6q] { border: 1px solid var(--line); border-radius: 8px; padding: 11px 13px; background: var(--card); cursor: help; }
.pf-k[b-fi456nyy6q] { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--mut2); }
.pf-v[b-fi456nyy6q] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 21px; font-weight: 700; margin-top: 3px; }
.pf-v.pos[b-fi456nyy6q] { color: var(--pos); }
.pf-v.neg[b-fi456nyy6q] { color: var(--neg); }
.pf-v.ink[b-fi456nyy6q] { color: var(--ink); }
.pf-v.na[b-fi456nyy6q] { color: var(--mut2); display: flex; align-items: center; }
.pf-v.na .gate-glyph[b-fi456nyy6q] { font-weight: 400; }
.pf-sub[b-fi456nyy6q] { font-size: 12.5px; color: var(--mut2); margin-top: 1px; }
.pf-years[b-fi456nyy6q] { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--card); }
.pf-ytitle[b-fi456nyy6q] { font-family: 'Inter Tight', sans-serif; font-weight: 600; font-size: 17px; padding: 12px 16px 9px; }
/* The yearly table wears the app's table face (app.css's --table-*, D-298 UAT walk 2026-10-08, finding 2): the value cells in it, the
   header row over them; line-height: inherit keeps the line box the `font` shorthand would reset. YEAR (36.23px) holds
   its 90px track, and every other label a 1fr quarter of the rest (YEAR-END VALUE, the longest, 118.63px; headless
   Chrome against the shipped fonts, 2026-10-08). The year and the year-end value keep their own emphasis. */
.pf-yrow[b-fi456nyy6q] { display: grid; grid-template-columns: 90px 1fr 1fr 1fr 1fr; gap: 8px; padding: 7px 16px; align-items: center; border-bottom: 1px solid var(--line4); font: var(--table-cell-font); line-height: inherit; text-align: center; color: var(--ink); }
.pf-yrow .pos[b-fi456nyy6q] { color: var(--pos); }
.pf-yrow .neg[b-fi456nyy6q] { color: var(--neg); }
.pf-yhead[b-fi456nyy6q] { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); font: var(--table-head-font); line-height: inherit; letter-spacing: var(--table-head-tracking); text-transform: uppercase; color: var(--mut2); background: var(--bg); }
.pf-yr[b-fi456nyy6q] { font-weight: 600; }
.pf-yend[b-fi456nyy6q] { font-weight: 700; font-size: 13.5px; }
.pf-ynote[b-fi456nyy6q] { padding: 6px 16px 10px; font-size: 11.5px; color: var(--mut2); }
/* /Components/Binder/TransactionModal.razor.rz.scp.css */
.tx-scrim[b-x1masdl1dd] { position: fixed; inset: 0; z-index: 100; background: rgba(20, 19, 26, 0.45); display: flex; align-items: center; justify-content: center; }
.tx-panel[b-x1masdl1dd] { width: 400px; max-width: calc(100vw - 24px); max-height: calc(100vh - 24px); overflow-y: auto; background: var(--card); color: var(--ink); border-radius: 10px; padding: 18px 20px; display: flex; flex-direction: column; gap: 12px; box-shadow: 0 24px 64px rgba(20, 19, 26, 0.35); outline: none; }
/* Nothing in the panel shrinks (UAT finding 3, owner 2026-08-30: "I don't like the idea of things
   disappearing that I didn't specifically say disappear"). .tx-panel is a scrolling flex column, and a
   flex item's automatic minimum size is 0 rather than its content once it sets overflow — which .tx-kind
   does for its rounded corners. Past the max-height the BUY/SELL toggle was therefore the one child free
   to collapse, and it went to a thin bar while every sibling kept its content minimum. Floored here for
   every child, so a viewport too short scrolls the panel with all of its controls intact. */
.tx-panel > *[b-x1masdl1dd] { flex-shrink: 0; }
.tx-title-row[b-x1masdl1dd] { display: flex; justify-content: space-between; align-items: center; }
.tx-title[b-x1masdl1dd] { font-size: 15px; font-weight: 600; }
.tx-close[b-x1masdl1dd] { background: none; border: 0; color: var(--mut); font-size: 15px; cursor: pointer; }
.tx-kind[b-x1masdl1dd] { display: flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.tx-kind button[b-x1masdl1dd] { flex: 1; padding: 5px; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 11.5px; background: var(--card); color: var(--mut); border: 0; cursor: pointer; }
.tx-kind button:disabled[b-x1masdl1dd] { cursor: not-allowed; opacity: 0.55; }
.tx-kind-buy.is-on[b-x1masdl1dd] { background: var(--pos2); color: var(--on-pos2); }
.tx-kind-sell.is-on[b-x1masdl1dd] { background: var(--acc); color: var(--card); }
.tx-field[b-x1masdl1dd] { display: flex; flex-direction: column; gap: 4px; }
.tx-input[b-x1masdl1dd] { border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; background: var(--card); color: var(--ink); font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; }
.tx-input.is-linked[b-x1masdl1dd] { border-color: rgba(21, 122, 80, 0.45); }
/* After a Save attempt, each field that blocks it takes `.is-invalid` and its reason line, and is
   cleared per field as it is fixed (D-198 UAT finding 5). The treatment itself is app.css's one
   `.is-invalid` rule and is deliberately NOT restated here — D-213's sweep made it app-wide, so
   this modal is no longer a special case. No competing box-shadow on focus either: :focus-visible
   (app.css) sets outline only, so both survive together. */
/* Finding 18, ruling A (owner, 2026-09-02; supersedes finding 14's reserved slots): a flagged
   field's reason takes the field's own label line. The label row is a fixed 17px in BOTH
   states — the reason's 11.5px line box would otherwise be 17.25 against the label's 15.75 and
   the box would move 1.5px on flag — with no wrapping and hidden overflow, so it cannot grow
   however long a reason is; every shipped reason is measured to fit its cell at the 400px
   panel (the two qty ones were trimmed to fit their 90px cell); below ~424px viewports the
   panel narrows (its max-width: calc(100vw - 24px)) and a line clips with an ellipsis once
   its cell is narrower than it — the date reason first, at a ≈401px viewport — instead of
   wrapping: still no movement. The child combinator keeps .lot labels (the
   picker's rows, also <label>s inside a .tx-field) out of it — and out of the caption look this
   rule also carries (10.5px uppercase --mut2), which until D-301 row 16 was a separate descendant
   rule that reached the lot rows and drew them as captions against app.css's .lot. Only one span is ever in flow
   (the label text, or the reason once flagged), so the row needs no flex. Measured in
   headless Chrome on the rendered markup: BUY-new 492.5px, one-instance SELL-new 564.8px,
   flagged or not — 0px of movement on Save — D-215 (18). */
.tx-field > label[b-x1masdl1dd] { display: block; height: 17px; line-height: 17px; white-space: nowrap; overflow: hidden; font-size: 10.5px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--mut2); }
.tx-field > label > span[b-x1masdl1dd] { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: top; }
/* A grid item's automatic minimum is its content — the price input's own 183px, a nowrap
   reason's full width — which at 400px split the 1fr 1fr price/date row 183/169 and, narrower,
   pushed a flagged row out of the panel sideways. Zero lets the tracks be what the grid says
   (176/176) and lets the label's overflow: hidden actually clip (review of e2922bf). */
.tx-field[b-x1masdl1dd] { min-width: 0; }
.tx-field-err[b-x1masdl1dd] { font-size: 11.5px; color: var(--neg); text-transform: none; letter-spacing: 0; }
/* While flagged the label's own text leaves the line but not the accessible name (the
   visually-hidden idiom): the control keeps "Price paid" as its name, and names the reason as
   its description through aria-describedby — the reason span is aria-hidden, so it is not read
   twice. A sighted user sees the reason where the label was. */
.tx-field > label.is-flagged .tx-lbl[b-x1masdl1dd] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.tx-input:disabled[b-x1masdl1dd] { background: var(--bg); color: var(--mut2); }
/* No spinner on Qty and Price — Chrome's own up/down controls render light in dark mode; the same
   treatment as the remove modal's quantity field, at the owner's word (D-198 UAT, 2026-08-28). */
.tx-input[type="number"][b-x1masdl1dd] { appearance: textfield; -moz-appearance: textfield; }
.tx-input[type="number"][b-x1masdl1dd]::-webkit-outer-spin-button, .tx-input[type="number"][b-x1masdl1dd]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tx-card-hint[b-x1masdl1dd] { font-size: 11.5px; color: var(--mut2); }
/* UAT finding 11 (owner ruling A, 2026-09-01, D-215): linking must never resize the
   modal -- the idle teach line and the linked confirmation swap inside a fixed slot.
   Finding 12 (ruling B, same day) set the slot's height: the linked state is a compact
   card row -- 40x56 art beside the card's name and the linked line -- so the slot
   reserves the thumb's 56px and the idle line centres in it. Scoped to the card field
   (review of 1faba5a): the one-line SELL/edit hints keep plain .tx-card-hint -- nothing
   swaps under them and no art belongs there; the BUY-edit branch DOES carry the row,
   being a linked card too (finding 12 widened it there deliberately). */
/* Sized up on the owner's live look (finding 12 follow-on, 2026-09-01): 40x56 read too
   small — "a hair bigger", the box growing with it. And the row sat off-centre between
   its neighbours: 4px above (the .tx-field gap to the input) vs 12px below (the
   .tx-panel gap to the next field); margin-top: 8px makes both read 12px — measured
   4/12 → 12/12 in headless Chrome on this file's own constants. */
.tx-hint-slot[b-x1masdl1dd] { min-height: 67px; margin-top: 8px; display: flex; align-items: center; gap: 8px; }
.tx-thumb[b-x1masdl1dd] { width: 48px; height: 67px; border-radius: 3px; border: 1px solid var(--line); object-fit: cover; flex-shrink: 0; }
.tx-thumb-name[b-x1masdl1dd] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; color: var(--ink); }
.tx-linked-line[b-x1masdl1dd] { font-size: 11.5px; }   /* colour inherits --pos from .tx-card-hint.is-linked */
.tx-card-hint.is-linked[b-x1masdl1dd] { color: var(--pos); }
.tx-grader-note[b-x1masdl1dd] { font-size: 11.5px; color: var(--mut2); }
.tx-row-grade[b-x1masdl1dd] { display: grid; grid-template-columns: 1fr 90px; gap: 8px; }
.tx-two[b-x1masdl1dd] { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.tx-static[b-x1masdl1dd], .tx-edit-card[b-x1masdl1dd] { border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; background: var(--bg); font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; color: var(--ink); }
.tx-static.is-empty[b-x1masdl1dd] { color: var(--mut2); }
.tx-qty-max[b-x1masdl1dd] { text-transform: none; letter-spacing: 0; margin-left: 4px; }
.tx-field-hint[b-x1masdl1dd] { font-size: 11.5px; color: var(--mut2); }
.tx-panel .modal-lots[b-x1masdl1dd] { margin-top: 2px; }
.tx-helper[b-x1masdl1dd] { font-size: 11.5px; color: var(--mut2); }
/* The 3px --neg left rule is the app-wide error-message look (app.css, D-213); it is written here
   rather than picked up from there because a scoped `.tx-error[b-…]` outranks the global rule. */
.tx-error[b-x1masdl1dd] { font-size: 12.5px; color: var(--neg); border-left: 3px solid var(--neg); padding: 8px 10px; border-radius: 6px; background: rgba(193, 58, 58, 0.08); }
.tx-actions[b-x1masdl1dd] { display: flex; justify-content: flex-end; align-items: center; gap: 8px; }
/* Finding 18: the helper takes the row's left, the buttons keep the right. At 400px the full
   sentence (241px) does not fit beside the buttons (122px + gaps) on one line and wraps to two;
   the row is then 34.5px against the buttons' 33.5 — still ≈28px under the old helper + gap. */
.tx-actions .tx-helper[b-x1masdl1dd] { margin-right: auto; }
.tx-btn[b-x1masdl1dd] { padding: 6px 12px; border-radius: 6px; border: 1px solid var(--line); background: var(--card); color: var(--ink); font-size: 13px; cursor: pointer; }
.tx-btn-save[b-x1masdl1dd] { background: var(--acc); color: var(--card); border-color: var(--acc); }
.tx-btn-save:disabled[b-x1masdl1dd] { background: var(--mutbg); color: var(--mut2); border-color: transparent; cursor: not-allowed; }
/* D-284 slice 2: the Source screen field. The hint sits in the label's own line like Qty's note and turns
   --pos only while the value is the auto pre-fill (the rework's scrHintFg) — its words say so, colour never
   alone (brand.md §4). The tier note is --warnInk on --card, the pairing slice 1 gave the rules-changed row;
   ContrastTests carries those rows. The select takes .is-linked's green border while a screen is chosen. */
.tx-screen-hint.is-prefilled[b-x1masdl1dd] { color: var(--pos); }
.tx-screen-note[b-x1masdl1dd] { font-size: 11.5px; color: var(--warnInk); line-height: 1.45; }
/* /Components/Binder/TransactionsLedger.razor.rz.scp.css */
/* Owner ruling, 2026-08-27 (D-195, fifth UAT finding): the two Binder tables mirror the
   Browse/Set roster. Every rule below takes its declarations from RosterTable.razor.css
   -- and the table face from app.css's `--table-*` (D-298 UAT walk 2026-10-08, finding 2) --
   .roster-table (container), .rt-head/.rt-row (row box), .rt-head (sticky header type),
   .rt-head-cell/.rt-grip (grip), .rt-cell/.rt-cell-name (cell type). The roster has no
   title bar; that one element takes the Card page ledger's (SalesLedger .lg-toolbar), the
   shipped ledger-with-title. The roster's cells all centre, its name cell included. */

/* .roster-table */
.ledger[b-dfziexqk9m] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    overflow-x: clip;
}

/* SalesLedger .lg-toolbar / .lg-toolbar h2 / .lg-count. The header row's border-top is
   the line under this block, so it carries none of its own. */
.l-title[b-dfziexqk9m] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 13px 16px 10px;
    font: 600 17px/1.2 'Inter Tight', sans-serif;
}
.l-count[b-dfziexqk9m] { font: 400 12.5px 'JetBrains Mono', monospace; color: var(--mut); }

/* .rt-head, .rt-row: one box for every row kind, no column gap. */
.l-head[b-dfziexqk9m], .l-row[b-dfziexqk9m] { display: grid; align-items: center; padding: 6px 16px; }

/* .rt-head: pinned below the 48px nav (z-index 10 under the nav's 20), solid --card so rows
   passing beneath are masked. The head is not an .l-row, so no cell rule below reaches it
   (the defect D-195's third finding fixed). */
.l-head[b-dfziexqk9m] {
    position: sticky;
    top: 48px;
    z-index: 10;
    background: var(--card);
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    font: var(--table-head-font);
    letter-spacing: var(--table-head-tracking);
    text-transform: uppercase;
    color: var(--mut2);
}

/* .rt-head-cell (these headers do not sort: no pointer, no hover). */
.l-head > div[b-dfziexqk9m] { position: relative; text-align: center; user-select: none; }

/* .rt-grip */
.l-grip[b-dfziexqk9m] {
    position: absolute;
    right: 0;
    top: 0;
    cursor: col-resize;
    color: var(--line3);
    touch-action: none;
}

/* .rt-row */
.l-row[b-dfziexqk9m] { border-bottom: 1px solid var(--line4); }
/* Ruling R-j (D-298 UAT walk 2026-10-08, finding 5, "all the corners"): the last row reaches the box's rounded foot and paints nothing
   there but its rule, which sits 1px above the box's own bottom border, only doubles it, and ran its square ends across
   the curve. So the last row drops it -- the smaller honest change than rounding a second line inside the first. */
.l-row:last-child[b-dfziexqk9m] { border-bottom: none; }

/* .rt-cell / .rt-cell-name. min-width: 0 lets the ellipsis work inside a grid track. */
.l-row > div[b-dfziexqk9m] { min-width: 0; text-align: center; font: var(--table-cell-font); color: var(--mut); }
.l-row .l-card[b-dfziexqk9m] { font: 500 14px 'Inter Tight', sans-serif; color: var(--ink); }
.l-row .l-card[b-dfziexqk9m], .l-row .l-note[b-dfziexqk9m] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.l-card a[b-dfziexqk9m] { color: inherit; text-decoration: none; }
.l-card a:hover[b-dfziexqk9m] { text-decoration: underline; }

.l-edit button[b-dfziexqk9m] { background: none; border: 0; color: var(--mut2); font-size: 11px; cursor: pointer; }
.l-edit button:hover[b-dfziexqk9m] { color: var(--ink); }
.l-edit button + button[b-dfziexqk9m] { margin-left: 8px; }
/* Direction hue on hover only (docs/brand.md §4) -- delete is the destructive action, not a
   state, so --neg is used here as text under a user gesture, not as an ambient signal. */
.l-edit .l-delete:hover[b-dfziexqk9m] { color: var(--neg); }
.chip[b-dfziexqk9m] { display: inline-block; padding: 1px 7px; border-radius: 4px; font-size: 11px; font-weight: 600; border: 1px solid; }
.chip.buy[b-dfziexqk9m] { color: var(--pos); background: var(--posBg); border-color: rgba(24, 158, 99, 0.3); }
.chip.sell[b-dfziexqk9m] { color: var(--acc); background: var(--accBg); border-color: rgba(74, 99, 208, 0.3); }

/* D-284 slice 2: the Source screen cell — a link per screen a purchase names (a sale: the distinct screens of
   the lots it sold, joined by " · "), ellipsised like Card and Note. */
.l-row .l-screen[b-dfziexqk9m] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.l-screen a[b-dfziexqk9m] { color: inherit; text-decoration: none; }
.l-screen a:hover[b-dfziexqk9m] { text-decoration: underline; }
/* /Components/Breadcrumb.razor.rz.scp.css */
/* card.md Layout block #0: 13.5px, --mut2 container. Identical in all three prototypes. */
.breadcrumb[b-q4qfap3x4r] {
    font-size: 13.5px;
    color: var(--mut2);
    display: flex;
    align-items: center;
    gap: 6px;
}

/* The prototypes' breadcrumb links are muted (Card.dc.html:56, Set.dc.html:58,
   Character.dc.html:58 — all `color: var(--mut)`), never accent and never --link. The Set and
   Character pages used to say var(--link) here, which is the drift this component exists to end.

   app.css has no global `a` reset (AppChrome's tab documents the same), so without the
   text-decoration the anchor renders UA-underlined. */
.crumb-link[b-q4qfap3x4r] {
    color: var(--mut);
    text-decoration: none;
}

.crumb-link:not([href])[b-q4qfap3x4r] {
    cursor: not-allowed;
}

.crumb-sep[b-q4qfap3x4r] {
    color: var(--mut2);
}

/* card.md §5.1 item 5: leaf crumb is plain --ink text, not a link. Set and Character previously
   had no rule at all here, so their leaf inherited the container's --mut2 and read as muted. */
.crumb-leaf[b-q4qfap3x4r] {
    color: var(--ink);
}
/* /Components/Card/AnchorTierBanner.razor.rz.scp.css */
/* card.md §2.3.3 — the amber advisory. Tokens are the same --warnInk pair SignalsPanel.razor.css
   and GradingActivityPanel.razor.css use: a colour this page has already shown the visitor,
   meaning the same thing.

   No root margin: <main> is a flex column with gap: 16px (MainLayout.razor.css), every
   @Body root is one of its flex items, and flex-item margins add to the gap rather than
   collapsing into it. Every sibling panel root leaves the spacing to that gap; .price-chart
   below declares no margin-top of its own, so a margin here would show up as an asymmetric
   band with more air above it than below.

   No focus rule either: app.css §4.10's app-wide :focus-visible ring covers the dismiss
   button like every other control. */
.anchor-notice[b-pn1eowt51z] {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: 13px;
    color: var(--ink);
    background: var(--warnBg);
    border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
    box-sizing: border-box;
}

.anchor-notice-glyph[b-pn1eowt51z] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    width: 9px;
    flex-shrink: 0;
    color: var(--warnInk);
}

.anchor-notice-text[b-pn1eowt51z] {
    flex: 1;
    min-width: 0;
}

.anchor-notice-dismiss[b-pn1eowt51z] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    color: var(--warnInk);
    background: none;
    border: none;
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 4px;
}
/* /Components/Card/BinderButton.razor.rz.scp.css */
/* card.md §2.2: Row A actions are height 29px. The resting dress moves here from
   IdentityHeader.razor.css's ::deep .btn-binder rule (Task 15) -- this component now
   owns its own dress directly, the same move WatchlistPicker made for its own button
   (task 22). The is-active recipe is WatchlistPicker.razor.css's own verbatim -- one
   vocabulary for one meaning. is-flash rides the same tinted dress: the post-save flip
   IS the "you're in the binder now" state, just freshly arrived. */
.btn-binder[b-zpc9a5fwkv] {
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 6px;
    font-size: 13.5px;
    font-weight: 500;
    padding: 0 11px;
    height: 29px;
    cursor: pointer;
}

.btn-binder.is-active[b-zpc9a5fwkv],
.btn-binder.is-active:hover[b-zpc9a5fwkv],
.btn-binder.is-flash[b-zpc9a5fwkv] {
    background: var(--posBg);
    color: var(--pos);
    border-color: rgba(24, 158, 99, 0.35);
}

html[data-cvd="1"] .btn-binder.is-active[b-zpc9a5fwkv],
html[data-cvd="1"] .btn-binder.is-flash[b-zpc9a5fwkv] {
    border-color: rgba(0, 114, 178, 0.35);
}
/* /Components/Card/CensusBars.razor.rz.scp.css */
/* card.md §2.6 intro: card panel, padding 14px 16px. "Card panel" is this design's one
   shared idiom (background/border/radius), matching SalesLedger's .ledger-panel. */
.census-panel[b-fptr54i4ke] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 14px 16px;
}

/* card.md §2.6 left header: flex, baseline, gap 10px. */
.census-header[b-fptr54i4ke] {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.census-header h2[b-fptr54i4ke] {
    margin: 0;
}

.census-sub[b-fptr54i4ke] {
    font-size: 12.5px;
    color: var(--mut2);
}

/* card.md §2.6 :223-:231: flex row, flex-end, gap 14px, height 150px, margin-top 14px. */
.census-bars[b-fptr54i4ke] {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    height: 150px;
    margin-top: 14px;
}

/* Each column stacks value label above -> bar -> grade label below. */
.census-bar-col[b-fptr54i4ke] {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: 4px;
    min-width: 0;
}

.census-bar-value[b-fptr54i4ke] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11.5px;
    color: var(--mut);
}

.census-bar[b-fptr54i4ke] {
    width: 100%;
    border-radius: 3px 3px 0 0;
}

/* card.md §3.8 p.bg: hard-coded rgba by grader, not theme tokens (deliberately, per the
   spec -- these two colours do not follow light/dark palette swaps). */
.census-bar-psa[b-fptr54i4ke] {
    background: rgba(74, 99, 208, .55);
}

.census-bar-cgc[b-fptr54i4ke] {
    background: rgba(138, 138, 134, .45);
}

.census-bar-label[b-fptr54i4ke] {
    font-size: 11px;
    color: var(--mut2);
    white-space: nowrap;
}

/* The :232 summary line is CensusSentence now (D-102), which owns its styling. */
/* /Components/Card/CensusSentence.razor.rz.scp.css */
/* card.md §3.8/§3.9: 12.5px --mut2, margin-top 10 — the mockup's summary-line
   recipe (:232/:248), shared by both panels' sentences. */
.census-sentence[b-cu83slar3b] {
    font-size: 12.5px;
    color: var(--mut2);
    margin: 10px 0 0;
}

.cs-name[b-cu83slar3b] {
    margin-right: 5px;
}

/* brand.md §4: ◌ = pending/insufficient, --mut2, tooltip carries the unlock.
   JetBrains Mono explicitly: the sentence is Inter, which has no U+25CC (D-198 UAT finding 2). */
.cs-gate[b-cu83slar3b] {
    cursor: help;
    font-family: 'JetBrains Mono', monospace;
    margin-right: 5px;
}

/* The mockup sets value and number runs in the mono face, one step less muted
   than the sentence body (:232/:248: mono, --mut). */
.cs-mono[b-cu83slar3b] {
    font-family: 'JetBrains Mono', monospace;
    color: var(--mut);
}

/* Market-meaning tones on number runs (§3.8/§3.9's sign convention: coloured
   by meaning, never by arithmetic sign). They override the mono grey. */
.cs-tone-pos[b-cu83slar3b] {
    color: var(--pos);
}

.cs-tone-neg[b-cu83slar3b] {
    color: var(--neg);
}
/* /Components/Card/DelistedChip.razor.rz.scp.css */
/* Muted -- this is a factual annotation, not a warning (card.md §3.1). */
.chip-delisted[b-0yblo195xb] {
    display: inline-flex;
    align-items: center;
    width: fit-content;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11.5px;
    font-weight: 500;
    padding: 1px 6px;
    border-radius: 4px;
    border: 1px solid var(--line);
    background: var(--mutbg);
    color: var(--mut2);
    cursor: help;
}
/* /Components/Card/FreshnessFooter.razor.rz.scp.css */
/* card.md §2.7: flex row, gap 16px, --mutbg background, 1px --line border, radius 8,
   padding 9px 14px, 12.5px --mut. Content is left-aligned; a flex:1 spacer pushes the
   attribution to the right (task 19 binding: "attribution right-aligned"). */
.freshness-footer[b-3w8r0ktxwp] {
    display: flex;
    align-items: center;
    gap: 16px;
    background: var(--mutbg);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 9px 14px;
    font-size: 12.5px;
    color: var(--mut);
}

.freshness-stamp[b-3w8r0ktxwp] {
    cursor: help;
}

.freshness-mono[b-3w8r0ktxwp] {
    font-family: 'JetBrains Mono', monospace;
}

.freshness-sep[b-3w8r0ktxwp] {
    color: var(--mut2);
}

.freshness-flex-spacer[b-3w8r0ktxwp] {
    flex: 1;
}

.freshness-about[b-3w8r0ktxwp] {
    color: var(--link);
    text-decoration: none;
}

.freshness-attribution[b-3w8r0ktxwp] {
    color: var(--mut2);
}

.freshness-attribution a[b-3w8r0ktxwp] {
    color: var(--mut2);
}
/* /Components/Card/GradingActivityPanel.razor.rz.scp.css */
.activity-panel[b-b4qf3bqsc0] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 14px 16px;
}

.activity-header[b-b4qf3bqsc0] {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.activity-header h2[b-b4qf3bqsc0] {
    margin: 0;
}

/* card.md §2.6 :237: mono 11px/600, letter-spacing .06em, --warnInk on
   rgba(176,127,26,.12), radius 3, padding 1px 5px, cursor help. */
.obs-badge[b-b4qf3bqsc0] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--warnInk);
    background: rgba(176, 127, 26, .12);
    border-radius: 3px;
    padding: 1px 5px;
    cursor: help;
}

/* D-094: the ghost delta chart. Same row geometry as the population panel
   (§2.6: flex-end, 150px, bars bottom-aligned, labels below), gap 10 per the
   prototype's activity row. */
.ga-bars[b-b4qf3bqsc0] {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    height: 150px;
    margin-top: 14px;
}

.ga-bar-col[b-b4qf3bqsc0] {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: 4px;
    min-width: 0;
}

.ga-bar[b-b4qf3bqsc0] {
    width: 100%;
    box-sizing: border-box;
}

/* Observed: the prototype's accent fill (§3.9 d.bg — a literal rgba, not a
   theme token, same as the population panel's PSA bars). */
.ga-bar-observed[b-b4qf3bqsc0] {
    background: rgba(74, 99, 208, 0.55);
    cursor: help;
}

/* Ghost: dashed outline, no fill, uniform height — uniform carries no data
   claim, and the tooltip names the month's unlock. */
.ga-bar-pending[b-b4qf3bqsc0] {
    height: 44px;
    border: 1.5px dashed var(--line3);
    border-radius: 2px 2px 0 0;
    cursor: help;
}

.ga-bar-value[b-b4qf3bqsc0] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11.5px;
    color: var(--mut);
}

.ga-bar-label[b-b4qf3bqsc0] {
    font-size: 11px;
    color: var(--mut2);
    white-space: nowrap;
}

/* The D-087/D-093 metric-slot styles (.ga-metric*, .ga-state, .ga-seg-*) were
   deleted by D-102 — the panel foot is CensusSentence now, which owns its own
   sentence styling. */
/* /Components/Card/IdentityHeader.razor.rz.scp.css */
/* card.md Layout block #1: card panel, radius 10, padding 16, display:flex, gap 18px. */
.card-identity-panel[b-0ctmtq3zv2] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 16px;
    display: flex;
    gap: 18px;
    position: relative;
}

/* D-246: preserve the complete source image at its natural aspect ratio.
   The signals panel must never determine the art's height. */
.art-col[b-0ctmtq3zv2] {
    flex-shrink: 0;
    width: 217px;
    align-self: flex-end;
    display: flex;
    flex-direction: column;
    padding: 0;
    border: none;
    background: none;
    cursor: zoom-in;
    font: inherit;
}

.art-col img[b-0ctmtq3zv2] {
    width: 100%;
    height: auto;
    display: block;
    object-fit: contain;
    border-radius: 6px;
}

.art-placeholder[b-0ctmtq3zv2] {
    width: 100%;
    aspect-ratio: 325 / 450;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    background: var(--mutbg);
    color: var(--mut2);
    font-size: 13px;
    text-align: center;
    padding: 8px;
    box-sizing: border-box;
}

/* D-246: bottom-align the intact art and lower content; never stretch the image. */
.right-col[b-0ctmtq3zv2] {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 12px;
}

/* card.md §2.2 Row A: flex, align-items flex-start, gap 12. */
.row-a[b-0ctmtq3zv2] {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

.title-block[b-0ctmtq3zv2] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
}

.title-block h1[b-0ctmtq3zv2] {
    font-family: 'Inter Tight', sans-serif;
    font-weight: 700;
    font-size: 26px;
    letter-spacing: -0.01em;
    margin: 0;
    color: var(--ink);
}

/* card.md §2.2: 14.5px --mut, margin-top 3px. The set segment is a deferred control,
   never plain text (carried Task 14 review finding) -- ::deep reaches into its
   isolated button so it reads as inline copy, not a stray chrome button. */
.subline[b-0ctmtq3zv2] {
    font-size: 14.5px;
    color: var(--mut);
    margin: 3px 0 0;
    display: flex;
    align-items: baseline;
    gap: 4px;
}

/* D-122: real anchors now (their target pages exist), rendered by this component —
   accent, quiet until hover, underline on hover like every live crumb. app.css has no
   global `a` reset, so text-decoration must be stripped here. */
.subline-set[b-0ctmtq3zv2],
.subline-character[b-0ctmtq3zv2] {
    font: inherit;
    color: var(--acc);
    padding: 0;
    border: none;
    background: none;
    text-decoration: none;
}

.subline-set:hover[b-0ctmtq3zv2],
.subline-character:hover[b-0ctmtq3zv2] {
    text-decoration: underline;
}

.subline-species-sep[b-0ctmtq3zv2] {
    color: var(--mut2);
}

.subline-number[b-0ctmtq3zv2] {
    white-space: nowrap;
}

/* The tier grid and signals panel wrap independently and align at their bottoms. */
.tiles-and-signals[b-0ctmtq3zv2] {
    flex: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    align-items: flex-end;
}

.row-a-actions[b-0ctmtq3zv2] {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
    margin-left: auto;
}

.actions-row[b-0ctmtq3zv2] {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* card.md §2.2: all Row A actions are height 29px, flex-shrink 0. */
.row-a-actions[b-0ctmtq3zv2]  .deferred {
    height: 29px;
    flex-shrink: 0;
}

.row-a-actions[b-0ctmtq3zv2]  .btn-open-charts {
    background: var(--btn);
    color: #FFFFFF;
    border-radius: 6px;
    font-size: 13.5px;
    font-weight: 600;
    padding: 0 12px;
    border: none;
}

/* The binder button's own dress (card bg, line border, ink text; is-active/is-flash
   tint) now lives in BinderButton.razor.css instead of reaching in via ::deep -- Task
   15 made it a first-party sibling, the same move task 22 made for the watchlist
   picker. Only the Row A sizing contract stays here. */
.row-a-actions[b-0ctmtq3zv2]  .btn-binder {
    flex-shrink: 0;
}

/* WatchlistPicker's own root (.watch-control) needs the same Row A sizing contract
   its DeferredControl-based predecessor got for free from the .deferred rule above --
   it is a first-party sibling now, not routed through ::deep. */
.row-a-actions[b-0ctmtq3zv2]  .watch-control {
    flex-shrink: 0;
}
/* /Components/Card/Lightbox.razor.rz.scp.css */
/* card.md §5.3 line :102: fixed, covers the viewport, z-index 200, zoom-out cursor. */
.lightbox-backdrop[b-am1v6y7dtq] {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.6);
    cursor: zoom-out;
}

/* card.md §5.3 line :103: width min(62vh, 78vw), aspect-ratio 325/450, default cursor. */
.lightbox-art[b-am1v6y7dtq] {
    position: relative;
    width: min(62vh, 78vw);
    aspect-ratio: 325 / 450;
    cursor: default;
}

.lightbox-art img[b-am1v6y7dtq] {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 10px;
    background: var(--card);
}

/* card.md §5.3 line :105: 30px circle, deliberately overlapping the art's corner. */
.lightbox-close[b-am1v6y7dtq] {
    position: absolute;
    top: -14px;
    right: -14px;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: 1px solid var(--line);
    background: var(--card);
    color: var(--ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.lightbox-close:hover[b-am1v6y7dtq] {
    background: var(--mutbg);
}
/* /Components/Card/PositionLine.razor.rz.scp.css */
/* The rework's own row (Cardstock Card.rework-2026-09-15.html:68-69): a wrapping 8px-gap row,
   7px under the line above it -- .title-block's 4px gap plus this 3px, the arithmetic .subline
   already uses one element up (IdentityHeader.razor.css). */
/* max-width is load-bearing, and measured: .title-block is a column flex container with
   align-items: flex-start, so this row is sized fit-content against the block's MAX-content width,
   not against the width it ends up with once .row-a's 380px action column has taken its share. At a
   1024px viewport the block settles at 323px while a 45-character screen name wants 344px, and
   without this clamp the row simply hangs 21px past the block instead of wrapping (headless Chrome,
   the shipped faces: .title-block clientWidth 323, scrollWidth 344). With it, the screen chip wraps
   under the money chip and then ellipsises its name, which is what the parts below are built for. */
.position-line[b-qmrirfl78c] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 3px 0 0;
    max-width: 100%;
}

/* The HELD chip, from the rework's :68: mono, 12px/600, radius 4, and --ink on hover. Its dress is the
   ACTIVE pill's (D-301 row 62, ruled 2026-10-18; HoldingsTable.razor.css's .h-chip.is-active): --pos on the
   --posBg tint over a --card layer of its own, with the pill's hairline, so HELD reads in one colour here,
   on the Screener and in the Binder, and the word carries the meaning beside the hue. The ground under it,
   .card-identity-panel, is --card, the pair ContrastTests pins (TintOverCardCssTests holds the layer).
   It never shrinks: every character in it is a number or a tier, so there is nothing here an ellipsis
   could take without taking a fact. */
.pl-chip[b-qmrirfl78c] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    font-weight: 600;
    color: var(--pos);
    background: linear-gradient(var(--posBg), var(--posBg)), var(--card);
    border: 1px solid color-mix(in srgb, var(--pos) 30%, transparent);
    border-radius: 4px;
    padding: 2px 7px;
    text-decoration: none;
}

.pl-chip:hover[b-qmrirfl78c] {
    color: var(--ink);
}

/* The second chip. Four arms share it: the screen's ACTIVE/EXITED chip, a named screen with nothing
   to say, and the two prompts that name no screen at all -- "no source screen · link it →" and
   "N source screens →", which take the rework's --mut2 (:69) because they are prompts rather than
   data. A screen's NAME is data and takes --mut below, the tone the gallery's own chip gives it
   (HoldingsGallery.razor.css:17). */
.pl-screen[b-qmrirfl78c] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: 12.5px;
    color: var(--mut2);
    text-decoration: none;
}

a.pl-screen:hover[b-qmrirfl78c] {
    text-decoration: underline;
}

/* Hue and glyph and word together (brand.md §4) -- ACTIVE and EXITED read the same in CVD mode and
   in greyscale, where ● and ▼ alone would not. So the name is the only part the ellipsis may eat,
   and it carries its own box for that: the mechanism the holdings table's chip ships
   (HoldingsTable.razor.css's .h-chip-name for the ellipsis box, .h-chip-glyph and .h-chip-state for the flex-shrink: 0 that keeps the
   glyph and the state word out of it), where ellipsising the whole chip instead ate the state word.
   Every arm that carries a name nests it in one of these, the unknown arm included: text-overflow does
   not reach the anonymous flex item a bare text child of .pl-screen would become. */
.pl-name[b-qmrirfl78c] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--mut);
}

.pl-glyph[b-qmrirfl78c] {
    flex-shrink: 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
}

.pl-state[b-qmrirfl78c] {
    flex-shrink: 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.05em;
}

/* The same filled pill the Binder's two chip surfaces take (HoldingsTable.razor.css). Found bare on the
   2026-09-19 walk, after those two shipped: this is the third surface of one membership and it had been left
   as text. .pl-chip is already the HELD badge beside it -- in this pill's own dress since D-301 row 62 -- so the
   state rides on .pl-screen -- whose other two
   arms, the unknown name and the "no source screen" prompt, are not memberships and stay bare, which is why
   the shape sits on the state rules and not on the shared one. This line is already Inter 12.5px, so only the
   fill and the shape are new. Ground measured live: .card-identity-panel is --card, the layer the tint wants. */
.pl-screen.is-active[b-qmrirfl78c],
.pl-screen.is-exited[b-qmrirfl78c] {
    padding: 2px 7px;
    border: 1px solid;
    border-radius: 4px;
}

.pl-screen.is-active[b-qmrirfl78c] {
    color: var(--pos);
    background: linear-gradient(var(--posBg), var(--posBg)), var(--card);
    border-color: color-mix(in srgb, var(--pos) 30%, transparent);
}

.pl-screen.is-exited[b-qmrirfl78c] {
    color: var(--neg);
    background: linear-gradient(var(--negBg), var(--negBg)), var(--card);
    border-color: color-mix(in srgb, var(--neg) 35%, transparent);
}

/* .pl-name pins --mut for the unknown arm, which is still data on the panel's own ground. Inside a pill the
   name is part of the state and takes its hue, or the chip would read in two colours. */
.pl-screen.is-active .pl-name[b-qmrirfl78c],
.pl-screen.is-exited .pl-name[b-qmrirfl78c] {
    color: inherit;
}

a.pl-screen.is-active:hover[b-qmrirfl78c],
a.pl-screen.is-exited:hover[b-qmrirfl78c] {
    text-decoration: none;
    filter: brightness(0.95);
}

/* The one unlinked arm -- a named screen the screen itself has nothing to say about -- explains
   itself in a tooltip, so it takes the help cursor from carrying one (the table's .h-scr span[title]). */
span.pl-screen[title][b-qmrirfl78c] {
    cursor: help;
}

/* The badge the holdings table and the gallery tile put on this same number (HoldingsTable.razor.css's .est,
   HoldingsGallery.razor.css's .est). Copied but for its margin-left: those two sit inline after a money
   string; this one is a flex item and takes the row's 8px gap.
   A scoped stylesheet cannot share a rule — each .razor.css compiles against its own component's scope
   attribute — but that does not make a fourth copy the only option: a shared badge component would carry
   the rule once, and copying is the choice made here rather than a constraint. The copies have already
   drifted: ActivityLog.razor.css's .est paints linear-gradient(var(--warnBg), var(--warnBg)), var(--card)
   where this one and the Binder's two paint a plain var(--warnBg). */
.est[b-qmrirfl78c] {
    display: inline-block;
    padding: 0 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--warnInk);
    background: var(--warnBg);
    border-radius: 3px;
    vertical-align: middle;
    cursor: help;
}
/* /Components/Card/PriceChart.razor.rz.scp.css */
/* card.md Layout block #3 + §2.4/§2.4.1/§5.4, values verbatim from
   Card.dc.html :110–:149. This file was missing entirely until 2026-08-13 —
   the component shipped class names with no stylesheet, so the panel had no
   card dress and the hover tooltip rendered as an unstyled in-flow div. */

.price-chart[b-399c9wo6oj] {
    --axis-gutter: 72px;
    --axis-gap: 8px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 14px 16px;
}

/* Header row (:111–:118). */
.pc-header[b-399c9wo6oj] {
    display: flex;
    align-items: baseline;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.pc-header h2[b-399c9wo6oj] {
    font-family: 'Inter Tight', sans-serif;
    font-weight: 600;
    font-size: 17px;
    margin: 0;
}

.pc-legend[b-399c9wo6oj] {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

/* Legend chips (:114): bare mono buttons, line swatch, ink on hover. Hidden
   series dim to 0.45 (the mockup's lg.op); the swatch color swap to --line3
   is inline from LegendSwatchColor. */
.pc-legend-chip[b-399c9wo6oj] {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12.5px;
    color: var(--mut);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}

.pc-legend-chip:hover:not(:disabled)[b-399c9wo6oj] {
    color: var(--ink);
}

.pc-legend-chip[aria-pressed="false"][b-399c9wo6oj] {
    opacity: 0.45;
}

.pc-legend-chip--muted[b-399c9wo6oj] {
    cursor: not-allowed;
    opacity: 0.45;
}

.pc-legend-swatch[b-399c9wo6oj] {
    width: 14px;
    height: 2.5px;
    border-radius: 1px;
    display: inline-block;
}

.pc-header[b-399c9wo6oj]  .pc-open-charts {
    margin-left: auto;
    font-size: 13px;
    background: none;
    border: none;
    padding: 0;
}

/* Plot row (:119–:143). */
.pc-body[b-399c9wo6oj] {
    /* Reserve half a month label plus breathing room at the final date. */
    padding-right: 28px;
    display: flex;
    gap: var(--axis-gap);
}

/* Y gutter (:120–:123): fixed width, two absolutely-positioned mono labels,
   right-aligned — the only tick labels that exist. 44px fit whole dollars; finding 15's
   cents (D-215, af39721 review) need more: 72px holds eleven 10.5px JetBrains Mono
   characters ("$999,999.99", ~69px measured). An axis bound at $1M+ still overflows
   left — one freak $16M corpus card today; content-sized gutter is a polish candidate. */
.pc-y-gutter[b-399c9wo6oj] {
    width: var(--axis-gutter);
    flex-shrink: 0;
    position: relative;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10.5px;
    color: var(--mut2);
}

.pc-y-max[b-399c9wo6oj],
.pc-y-min[b-399c9wo6oj] {
    position: absolute;
    right: 0;
}

.pc-y-max[b-399c9wo6oj] {
    top: 2px;
}

.pc-y-min[b-399c9wo6oj] {
    bottom: 2px;
}

.pc-plot[b-399c9wo6oj] {
    flex: 1;
    min-width: 0;
    position: relative;
    cursor: crosshair;
}

.pc-canvas[b-399c9wo6oj] {
    height: 230px;
}

/* §2.4.1: the hollow current-month dot — 8px, card-hollow, riding the plot's
   current-date coordinate; border-color is inline (it wears the dot series' colour). */
.pc-dot[b-399c9wo6oj] {
    position: absolute;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--card);
    border: 1.5px solid var(--acc);
    left: var(--month-last-x);
    transform: translate(-50%, -50%);
    box-sizing: border-box;
}

/* Crosshair tooltip (:135): pinned top-left, never intercepts the pointer. */
.pc-tooltip[b-399c9wo6oj] {
    position: absolute;
    top: 8px;
    left: 8px;
    background: var(--tooltipBg);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 5px 9px;
    pointer-events: none;
    box-shadow: 0 4px 12px rgba(20, 19, 26, 0.08);
    z-index: 5;
}

.pc-tooltip-month[b-399c9wo6oj] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    color: var(--mut2);
}

.pc-tooltip-row[b-399c9wo6oj] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 12.5px;
    font-weight: 400;
}

.pc-tooltip-row--bold[b-399c9wo6oj] {
    font-weight: 700;
}

/* Mockup: three month labels immediately beneath the plot, no footer. */
.pc-x-labels[b-399c9wo6oj] {
    position: relative;
    height: 15px;
    margin-top: 4px;
    margin-left: calc(var(--axis-gutter) + var(--axis-gap));
    margin-right: 28px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10.5px;
    color: var(--mut2);
}

.pc-x-labels span[b-399c9wo6oj] {
    position: absolute;
    transform: translateX(-50%);
    white-space: nowrap;
}
.pc-x-first[b-399c9wo6oj] { left: var(--month-first-x); }
.pc-x-middle[b-399c9wo6oj] { left: var(--month-middle-x); }
.pc-x-last[b-399c9wo6oj] { left: var(--month-last-x); }

.pc-tooltip-detail[b-399c9wo6oj] {
    display: block;
    font-size: 10.5px;
    font-weight: 400;
    color: var(--mut2);
}


/* Mockup's single faint mid-height reference rule. Non-interactive above the canvas. */
.pc-plot[b-399c9wo6oj]::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 1px;
    background: var(--line4);
    pointer-events: none;
}

/* D-251: the vertical cursor shares the tooltip's snapped monthly coordinate. */
.pc-crosshair[b-399c9wo6oj] {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--mut2);
    opacity: .4;
    pointer-events: none;
}
/* /Components/Card/SalesLedger.razor.rz.scp.css */
/* card.md §2.5/§3.7: card panel, radius 10, no padding -- rows are full-bleed with internal
   16px. Toolbar, header and body share one grid-template-columns string (R-6) so a resize
   applies to both atomically. */

.ledger-panel[b-5ljhyhvteu] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
}

/* The app-wide error look (app.css, D-213): --neg text and a 3px --neg left rule, no glyph.
   Written here because a scoped `.lg-error[b-…]` outranks the global rule, and because this one
   is a ledger panel — it keeps .ledger-panel's border, radius and ground; only the left edge
   thickens. Its 16px padding already clears the bar, and .lg-retry sets its own --ink. */
.lg-error[b-5ljhyhvteu] {
    padding: 16px;
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--neg);
    border-left: 3px solid var(--neg);
}

.lg-retry[b-5ljhyhvteu] {
    height: 29px;
    padding: 0 10px;
    border-radius: 6px;
    border: 1px solid var(--line);
    background: var(--card);
    color: var(--ink);
    cursor: pointer;
}

.lg-toolbar[b-5ljhyhvteu] {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 13px 16px 10px;
}

.lg-toolbar h2[b-5ljhyhvteu] {
    font: 600 17px/1.2 "Inter Tight", sans-serif;
    margin: 0;
}

.lg-chips[b-5ljhyhvteu] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.lg-count[b-5ljhyhvteu] {
    margin-left: auto;
    font-family: "JetBrains Mono", monospace;
    font-size: 12.5px;
    color: var(--mut);
    cursor: help;
    white-space: nowrap;
}

.lg-chip[b-5ljhyhvteu], .lg-subchip[b-5ljhyhvteu] {
    font-family: "JetBrains Mono", monospace;
    font-weight: 600;
    background: var(--card);
    color: var(--mut);
    border: 1px solid var(--line);
    cursor: pointer;
}

.lg-chip[b-5ljhyhvteu] {
    height: 24px;
    border-radius: 5px;
    padding: 0 9px;
    font-size: 12px;
}

.lg-subchip[b-5ljhyhvteu] {
    height: 22px;
    border-radius: 4px;
    padding: 0 7px;
    font-size: 11.5px;
}

.lg-chip-sel[b-5ljhyhvteu] {
    background: var(--acc);
    color: var(--card);
    border-color: var(--acc);
}

.lg-group[b-5ljhyhvteu] {
    position: relative;
}

.lg-popover[b-5ljhyhvteu] {
    position: absolute;
    top: 26px;
    left: 0;
    z-index: 50;
    min-width: 240px;
    padding: 8px 9px;
    border-radius: 8px;
    background: var(--card);
    border: 1px solid var(--line);
    box-shadow: 0 8px 24px rgba(20, 19, 26, 0.13);
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

.lg-pop-header[b-5ljhyhvteu] {
    flex-basis: 100%;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--mut2);
    margin-bottom: 5px;
}

.lg-row[b-5ljhyhvteu] {
    display: grid;
    gap: 8px;
}

/* The app's table face (app.css's --table-head-*, D-298 UAT walk 2026-10-08, finding 2); line-height: inherit keeps the line box the
   `font` shorthand would reset. At 12.5px every label holds its track with the grip clear at the default widths
   (measured in headless Chrome against the shipped fonts, 2026-10-08, each with its sort arrow: DATE ▾ 45.22px in 96,
   TIER ▾ 40.31 in 108, REALIZED ▾ 75.47 in 92, SOURCE ▾ 65.98 in 92, LISTING TITLE ▾ 105.39 in the 160px floor). */
.lg-header[b-5ljhyhvteu] {
    padding: 7px 16px;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background: var(--bg);
    font: var(--table-head-font);
    line-height: inherit;
    letter-spacing: var(--table-head-tracking);
    color: var(--mut2);
    text-transform: uppercase;
}

.lg-h[b-5ljhyhvteu] {
    display: flex;
    align-items: center;
    cursor: pointer;
}

.lg-h:hover[b-5ljhyhvteu] {
    color: var(--acc);
}

/* §5.5 #30: the label takes the whole cell width minus the grip, centred. */
.lg-h-label[b-5ljhyhvteu] {
    flex: 1;
    text-align: center;
}

.lg-grip[b-5ljhyhvteu] {
    width: 6px;
    margin-right: -6px;
    flex-shrink: 0;
    cursor: col-resize;
    color: var(--line3);
}

.lg-grip:hover[b-5ljhyhvteu] {
    color: var(--acc);
}

.lg-body[b-5ljhyhvteu] {
    padding: 6px 16px;
    align-items: center;
    border-bottom: 1px solid var(--line4);
}

/* Ruling R-j (D-298 UAT walk 2026-10-08, finding 5, "all the corners"): the last row reaches the box's rounded foot and paints nothing
   there but its rule, which sits 1px above the box's own bottom border, only doubles it, and ran its square ends across
   the curve. So the last row drops it -- the smaller honest change than rounding a second line inside the first. Under
   the pager the row is not the last child and keeps the rule that parts it from the pager. */
.lg-body:last-child[b-5ljhyhvteu] {
    border-bottom: none;
}

/* The value cells wear the table face (app.css's --table-cell-font); the price keeps its own emphasis below, and the
   listing title is prose in its own face. */
.lg-date[b-5ljhyhvteu], .lg-tier[b-5ljhyhvteu], .lg-price[b-5ljhyhvteu], .lg-src[b-5ljhyhvteu] {
    font: var(--table-cell-font);
    line-height: inherit;
    text-align: center;
}

.lg-date[b-5ljhyhvteu], .lg-src[b-5ljhyhvteu] {
    color: var(--mut);
}

.lg-tier[b-5ljhyhvteu] {
    color: var(--ink);
}

.lg-price[b-5ljhyhvteu] {
    font-size: 13.5px;
    font-weight: 700;
    border-bottom: none;
    cursor: default;
}

.lg-listed[b-5ljhyhvteu] {
    border-bottom: 2px dotted var(--warnInk);
    cursor: help;
}

/* D-219 (b)'s absent-money dash, the same three declarations CharacterPage's .stat-unpriced and
   BrowsePage's .fan-unpriced/.sp-unpriced carry -- CSS isolation is per-file, so the rule is
   duplicated rather than shared. It sits INSIDE .lg-price, which sets 700 weight and a default
   cursor, so both have to be restated here or the dash would read as a bold price. */
.lg-unpriced[b-5ljhyhvteu] {
    color: var(--mut2);
    font-weight: 400;
    cursor: help;
}

.lg-title[b-5ljhyhvteu] {
    font-size: 13px;
    color: var(--mut);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.lg-empty[b-5ljhyhvteu] {
    padding: 26px 16px;
    text-align: center;
    font-size: 13.5px;
    color: var(--mut2);
}

/* /Components/Card/SignalsPanel.razor.rz.scp.css */
/* card.md §2.3.2: tile dress, flex 1 1 300px beside the tier tile grid. */
.sig-panel[b-0xnwhmxrm4] {
    flex: 1 1 300px;
    align-self: stretch;
    margin-top: calc(-1 * var(--signals-rise, 0px));
    min-width: 0;
    box-sizing: border-box;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 10px 12px 9px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sig-header[b-0xnwhmxrm4] {
    display: flex;
    align-items: baseline;
    gap: 8px;
}

.sig-title[b-0xnwhmxrm4] {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--mut2);
    text-transform: uppercase;
}

/* D-191: the selected tier's label, in ink -- the panel's one statement of which tier
   every row below was evaluated at. */
.sig-tier[b-0xnwhmxrm4] {
    color: var(--ink);
}

.sig-spacer[b-0xnwhmxrm4] {
    flex: 1;
}

.sig-count[b-0xnwhmxrm4] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    color: var(--mut2);
    cursor: help;
}

/* Main and liquidity rows share column sizing. */
.sig-rows[b-0xnwhmxrm4],
.sig-liquidity[b-0xnwhmxrm4] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
    gap: 2px 18px;
    align-content: start;
}

.sig-rows[b-0xnwhmxrm4] {
    flex: 1;
    align-content: stretch;
    grid-auto-rows: minmax(min-content, 1fr);
}

.sig-row[b-0xnwhmxrm4] {
    display: flex;
    align-items: baseline;
    gap: 7px;
    padding: 1.5px 0;
    cursor: help;
    min-width: 0;
}

.sig-glyph[b-0xnwhmxrm4] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    width: 9px;
    flex-shrink: 0;
}

.sig-name[b-0xnwhmxrm4] {
    font-size: 12.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Keep the direction cue with its value; colour is never the only cue. */
.sig-result[b-0xnwhmxrm4] {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    flex-shrink: 0;
}

/* The liquidity group (D-243 ruling 10): counting is not a price signal, so it sits apart from
   the price family rather than among it. A rule above it and a named heading -- the separation
   has to be legible as a different family, not as a gap. */
.sig-liquidity[b-0xnwhmxrm4] {
    border-top: 1px solid var(--line);
    padding-top: 6px;
}

.sig-group[b-0xnwhmxrm4] {
    grid-column: 1 / -1;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--mut2);
    text-transform: uppercase;
}

.sig-value[b-0xnwhmxrm4] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11.5px;
    font-weight: 500;
    white-space: nowrap;
}

/* Row fg by state (card.md §2.3.2 table): the glyph and value wear the row's fg;
   the name carries its own colour per state. Colour never alone -- the glyph is
   text, so every state survives colourblind mode and reads aloud. */
.sig-firing.sig-tone-pos[b-0xnwhmxrm4] { color: var(--pos); }
.sig-firing.sig-tone-neg[b-0xnwhmxrm4] { color: var(--neg); }
.sig-firing.sig-tone-caution[b-0xnwhmxrm4] { color: var(--warnInk); }
.sig-firing .sig-name[b-0xnwhmxrm4] { color: var(--ink); }

.sig-neutral[b-0xnwhmxrm4] { color: var(--mut); }
.sig-neutral .sig-name[b-0xnwhmxrm4] { color: var(--ink); }

.sig-quiet[b-0xnwhmxrm4],
.sig-belowfloor[b-0xnwhmxrm4] { color: var(--mut2); }
.sig-quiet .sig-name[b-0xnwhmxrm4],
.sig-belowfloor .sig-name[b-0xnwhmxrm4] { color: var(--mut); }

.sig-locked[b-0xnwhmxrm4] { color: var(--mut2); }
.sig-locked .sig-name[b-0xnwhmxrm4] { color: var(--mut2); }

.sig-footer[b-0xnwhmxrm4] {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.sig-footer[b-0xnwhmxrm4]  .sig-charts-link {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    padding: 0;
    border: none;
    background: none;
}

/* D-128 firing breakdown — tone chips (owner pick): tinted shell, toned glyph, count
   in ink. The glyph carries the meaning; the tint is redundant with it. */
.sig-bd-chip[b-0xnwhmxrm4] { display: inline-flex; align-items: center; gap: 3px; padding: 0 7px;
    border-radius: 99px; font-size: 12px; border: 1px solid; color: var(--ink); }
.sig-bd-chip-pos[b-0xnwhmxrm4] { background: color-mix(in srgb, var(--pos) 8%, transparent);
    border-color: color-mix(in srgb, var(--pos) 35%, transparent); }
.sig-bd-chip-neg[b-0xnwhmxrm4] { background: color-mix(in srgb, var(--neg) 8%, transparent);
    border-color: color-mix(in srgb, var(--neg) 35%, transparent); }
.sig-bd-chip-warn[b-0xnwhmxrm4] { background: color-mix(in srgb, var(--warn) 8%, transparent);
    border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.sig-bd-pos[b-0xnwhmxrm4] { color: var(--pos); }
.sig-bd-neg[b-0xnwhmxrm4] { color: var(--neg); }
.sig-bd-warn[b-0xnwhmxrm4] { color: var(--warn); }

.sig-more[b-0xnwhmxrm4] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    color: var(--mut2);
}
/* /Components/Card/TierStrip.razor.rz.scp.css */
/* D-246: fixed 140×100 tiles, three columns, with an actual 8px row gap.
   The signals panel can grow without distributing space between these rows. */
.tier-strip[b-yb235ucxzr] {
    display: grid;
    grid-template-columns: repeat(3, 140px);
    grid-auto-rows: 100px;
    gap: 8px;
    flex: 0 0 auto;
}

/* Tile dress: --bg fill, 1px --line, radius 8, padding 10px 11px; label at the
   head, price and change at the foot via space-between. The tile is a <button>
   (D-191): app.css's button reset gives font/color inherit and strips the UA
   background and border, and this rule -- more specific than the bare element
   selector -- puts the tile's own back. text-align resets the UA's centering. */
.tier-cell[b-yb235ucxzr] {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 10px 11px;
    min-width: 0;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    text-align: left;
    width: 100%;
    margin: 0;
}

.tier-cell:hover:not(:disabled):not([aria-pressed="true"])[b-yb235ucxzr] {
    background: var(--hov);
}

/* No series (card.md §2.3, D-191): the chart legend's muted recipe -- present, not
   clickable, opacity .45. The price and change already read "—". */
.tier-cell:disabled[b-yb235ucxzr] {
    cursor: not-allowed;
    opacity: .45;
}

/* Selected (owner, 2026-08-26, D-191): --selBg fill, 1px --acc border, label to ink;
   the numbers keep their colours -- the hue lives in the shell, never the sentence
   (the banner's own ruling, D-182). Dark adds a 1px outer ring, making a 2px edge:
   the owner chose it from real-pixel candidates after the fill alone washed out. Light
   deliberately has none -- asked and ruled 2026-08-29 (D-212), because dark's fill is
   invisible at 1.01:1 against an unselected tile while light's border already carries the
   state at 4.13:1 against its neighbours. The asymmetry is the decision, not an oversight;
   do not unify the two for symmetry. The
   html[data-theme] form inside a scoped stylesheet is WatchlistPicker.razor.css's
   established pattern (its data-cvd override), verified in production (D-146). */
.tier-cell[aria-pressed="true"][b-yb235ucxzr] {
    background: var(--selBg);
    border-color: var(--acc);
}

.tier-cell[aria-pressed="true"] .tier-label[b-yb235ucxzr] {
    color: var(--ink);
}

html[data-theme="dark"] .tier-cell[aria-pressed="true"][b-yb235ucxzr] {
    box-shadow: 0 0 0 1px var(--acc);
}

.tier-label[b-yb235ucxzr] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--mut2);
    text-transform: uppercase;
    white-space: nowrap;
}

.tier-label-text[b-yb235ucxzr] {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* D-243: the price and the date it was sold on are one unit, so they sit in one flex
   child and the tile stays three children under space-between -- label at the head,
   value block in the middle, change at the foot, exactly as D-092 laid it out.

   HEADROOM, and it is tighter than it looks. These figures are arithmetic on the rules in
   this file plus app.css, corroborated by a headless render of the same rule set. The
   shipped tile itself has not been measured, so a rule that reconstruction missed would
   move them.

   The content box is 78px, not 80: grid-auto-rows is 100px and .tier-cell is
   box-sizing: border-box with a 1px border, so 100 - 2 borders - 20 padding.

   The lines are taller than their font-size. app.css:112 sets `button { font: inherit }`
   and the font shorthand carries body's line-height: 1.5 (app.css:82), so any line here
   that does not declare its own gets 1.5x. Reading the four off the declared sizes alone
   under-counts them by a third, which is the mistake this note exists to stop:
     .tier-label   11px, inherited 1.5          = 16.5
     .tier-price   19px, line-height 1          = 19
     .tier-asof    11px, line-height 1.2 + 3px margin = 16.2
     .tier-change  12px, inherited 1.5          = 18
                                          total = 69.7
   Slack is therefore 8.3px, which space-between splits into two ~4.2px gaps -- down from
   ~12.2px each before the caption line. It fits, with little to spare: a single wrap on
   any line would overflow, which is why .tier-label is nowrap and .tier-asof is sized to
   a date that cannot wrap. app.css's .band-actions-btn reads the same inheritance the same way. */
.tier-value[b-yb235ucxzr] {
    display: flex;
    flex-direction: column;
}

/* JetBrains Mono, 19px/700, line-height 1 (card.md §2.3 amended, D-092). The
   tile's space-between distributes the three children; no margins. */
.tier-price[b-yb235ucxzr] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 19px;
    font-weight: 700;
    line-height: 1;
    color: var(--ink);
    /* D-283: the roll's tint fades in and back out through this. Declared here so the fade
       survives the tint class being removed.

       1450ms, not the 450ms this shipped with, on the owner's UAT finding (2026-09-15): at 450ms the
       whole announcement was over inside 2.4s, so catching it in peripheral vision left nothing to
       confirm by the time you looked back -- "it kind of might mess with your head". The roll itself
       is deliberately NOT slowed ("I want the numbers to show up just as fast"); only the colour's
       departure is, which is what a delayed glance actually needs to find. */
    transition: color 1450ms ease;
}

/* The date the price was sold on (D-243). Mono because it is a number, 11px so it reads
   as a caption under the 19px price rather than as a second figure, --mut2 because it is
   the same muted supporting weight the tier label above it carries -- a pair ContrastTests
   already computes on --bg in all four modes. MM-DD-YYYY is 10 characters, comfortably
   inside the 116px content box the $99,999.99 sizing note above established, so it does
   not wrap. No uppercase: .tier-label's text-transform belongs to the label, not a date.
   A tile with no price prints the tile's dash here (D-301 row 58), so the line always holds
   a glyph at this rule's 13.2px line box and the six price rows stay level without a
   reserved height. */
.tier-asof[b-yb235ucxzr] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    line-height: 1.2;
    color: var(--mut2);
    margin-top: 3px;
}

/* JetBrains Mono, 12px (card.md §2.3 line 3). Colour keys off the formatted sign. */
.tier-change[b-yb235ucxzr] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    color: var(--mut2);
}

.tier-change-pos[b-yb235ucxzr] {
    color: var(--pos);
}

.tier-change-neg[b-yb235ucxzr] {
    color: var(--neg);
}
/* /Components/Card/WatchlistPicker.razor.rz.scp.css */
/* card.md §3.4/§5 #9: the split button toggles the popover only, never adds the card.
   .watch-control is the outside-mousedown WRAPPER (button + popover together, matched
   by [data-watch-pop] in site.js) -- SalesLedger's .lg-group/[data-lg-pop] is the
   established precedent for this shape. */
.watch-control[b-xqz9756bsy] {
    position: relative;
}

/* Row A: all actions height 29px, flex-shrink 0 (card.md §2.2). Resting dress mirrors
   IdentityHeader.razor.css's pre-existing .btn-watchlist/.btn-binder recipe verbatim
   (card bg, line border, ink text) -- this component now owns that dress directly
   instead of it living in the parent via ::deep. */
.watch-btn[b-xqz9756bsy] {
    height: 29px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 6px;
    font-size: 13.5px;
    font-weight: 500;
    padding: 0 11px;
    cursor: pointer;
    font-family: inherit;
}

.watch-btn:hover[b-xqz9756bsy] {
    background: var(--hov);
}

/* Active recipe (card.md §3.4): posBg(0.10) bg / PAL.pos fg / posBg(0.35) border,
   hover unchanged.

   Fix round 1, Important 4: this used to color-mix off var(--pos) -- wrong colour.
   The prototype's posBg(a) = rgba(24,158,99,a), constant across light/dark and
   switching only for CVD (app.css:105-112's own comment: "observed alpha ... .10
   .12 .25 light, .18 .20 dark" band all share ONE base rgb; --pos itself is a
   DIFFERENT, theme-adaptive colour, e.g. #4CC08D in dark standard -- confirmed by
   reading app.css's four-selector cascade directly). --posBg (app.css:110) is
   already exactly posBg(0.10), so the background uses it verbatim. .35 has no
   ready-made token (--posBg is fixed at .10), so the border composes the same rgb
   channels directly, per app.css:105-109's own documented convention for a
   different intensity -- with a CVD override alongside it so the border tracks
   --posBg's OWN cvd switch (app.css:168) rather than silently staying the
   non-colourblind-safe green under data-cvd="1". */
.watch-btn.is-active[b-xqz9756bsy],
.watch-btn.is-active:hover[b-xqz9756bsy] {
    background: var(--posBg);
    color: var(--pos);
    border-color: rgba(24, 158, 99, 0.35);
}

html[data-cvd="1"] .watch-btn.is-active[b-xqz9756bsy],
html[data-cvd="1"] .watch-btn.is-active:hover[b-xqz9756bsy] {
    border-color: rgba(0, 114, 178, 0.35);
}

/* Fix round 1, Important 5: Cardstock Card.dc.html:73 right-anchors the popover
   (`right: 0`, not `left: 0`) -- card.md §3.4 is silent, so the frozen prototype is
   the tiebreak. The control sits in a right-aligned action row, so a left-anchored
   popover would overhang the panel's own right border. min-width/padding/z-index
   below are also pulled from that same line (190 / 6px / 60), not SalesLedger's
   .lg-popover values, which this file's first draft mistakenly matched instead. */
.watch-popover[b-xqz9756bsy] {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 60;
    min-width: 190px;
    padding: 6px;
    border-radius: 8px;
    background: var(--card);
    border: 1px solid var(--line);
    box-shadow: 0 8px 24px rgba(20, 19, 26, 0.13);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* D-144's two sections. The headers are the only thing telling the visitor these rows
   answer different questions, so they are labels rather than decoration -- mono and
   uppercase, matching the eyebrow treatment brand.md §3 uses for the same job. */
.watch-secthead[b-xqz9756bsy] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 9.5px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--mut2);
    font-weight: 600;
    padding: 4px 2px 6px;
}

/* Tinted, because "you are watching this here" is a positive state and the same
   posBg the split button uses for it -- one vocabulary for one meaning. */
.watch-watching-row[b-xqz9756bsy] {
    padding: 7px 6px;
    border-radius: 5px;
    background: var(--posBg);
    margin-bottom: 4px;
}

.watch-watching-top[b-xqz9756bsy] {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    margin-bottom: 6px;
}

.watch-chips[b-xqz9756bsy] {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

/* Saved grades can add chips; long unknown labels wrap within the popover. */
.watch-chip[b-xqz9756bsy] {
    box-sizing: border-box;
    max-width: 100%;
    overflow-wrap: anywhere;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10.5px;
    padding: 3px 6px;
    border-radius: 4px;
    border: 1px solid var(--line3);
    background: var(--card);
    color: var(--mut2);
    min-width: 31px;
    text-align: center;
    cursor: pointer;
}

.watch-chip:hover:not(:disabled)[b-xqz9756bsy] {
    border-color: var(--mut3);
    color: var(--mut);
}

/* The foreground legal on a --pos2 fill, per mode. NOT a literal: a fixed ink was the
   first fix and it failed at 3.28:1 in light-CVD, where --pos2 becomes #0072B2 and the
   legal foreground flips to white — the one mode out of four that inverts. --on-pos2
   carries that per mode and ContrastTests recomputes every pair from the hexes. */
.watch-chip.is-on[b-xqz9756bsy] {
    background: var(--pos2);
    border-color: var(--pos2);
    color: var(--on-pos2);
    font-weight: 600;
}

.watch-chip:disabled[b-xqz9756bsy] {
    opacity: 0.55;
    cursor: default;
}

.watch-filter[b-xqz9756bsy] {
    width: 100%;
    box-sizing: border-box;
    height: 27px;
    border: 1px solid var(--line2);
    border-radius: 6px;
    background: var(--inbg);
    color: var(--ink);
    font-family: inherit;
    font-size: 12.5px;
    padding: 0 8px;
    margin-bottom: 6px;
}

/* Capped rather than unbounded: at the 20-list cap the addable section is the part
   that grows, and a popover taller than the viewport cannot be closed by clicking
   outside it. */
.watch-add-scroll[b-xqz9756bsy] {
    max-height: 186px;
    overflow-y: auto;
}

.watch-no-match[b-xqz9756bsy] {
    font-size: 12.5px;
    color: var(--mut2);
    padding: 8px 6px;
}

.watch-rows[b-xqz9756bsy] {
    display: flex;
    flex-direction: column;
}

/* Each row is a reset <button role="checkbox">, mirroring FilterPopover's
   .pf-option (also role=checkbox + a decorative box span) rather than a real
   <input type=checkbox> -- card.md's boxBg/boxBd table describes a styled box, not a
   native control. Padding is Cardstock Card.dc.html:74's literal `6px 8px` (fix round 2
   -- this file's first draft had 6px 4px instead). */
.watch-row[b-xqz9756bsy] {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px;
    border: none;
    background: none;
    border-radius: 4px;
    font: inherit;
    color: var(--ink);
    cursor: pointer;
    text-align: left;
}

.watch-row:hover[b-xqz9756bsy] {
    background: var(--hov);
}

.watch-row:disabled[b-xqz9756bsy] {
    opacity: .6;
    cursor: default;
}

/* card.md §3.4: 15×15px, 1.5px border, radius 4. Checked = PAL.pos2 fill (both bg and
   border); unchecked = card bg, line3 border. */
.watch-checkbox[b-xqz9756bsy] {
    flex-shrink: 0;
    width: 15px;
    height: 15px;
    box-sizing: border-box;
    border-radius: 4px;
    border: 1.5px solid var(--line3);
    background: var(--card);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    line-height: 1;
    /* --pos2 brightens in dark mode (brand.md §2.4) rather than staying dark like --btn
       does, so a fixed white glyph would lose contrast there. var(--card) "punches
       through" to the page's own background colour instead, which contrasts against
       --pos2 in every theme/CVD combination by construction. */
    color: var(--card);
}

.watch-checkbox.is-checked[b-xqz9756bsy] {
    background: var(--pos2);
    border-color: var(--pos2);
}

.watch-row-name[b-xqz9756bsy] {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
}

.watch-row-count[b-xqz9756bsy] {
    flex-shrink: 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 11.5px;
    color: var(--mut2);
}

/* Fix round 1: Cardstock Card.dc.html:76's divider between the list rows and
   "+ New list…" was missing entirely. */
.watch-new-list-divider[b-xqz9756bsy] {
    border-top: 1px solid var(--line4);
    margin: 5px 2px;
}

/* Fix round 1: the prototype's "+ New list…" is var(--acc), not the muted grey every
   other secondary row text uses -- it reads as an action, not a label. */
.watch-new-list-row[b-xqz9756bsy] {
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    padding: 6px 4px;
    border: none;
    background: none;
    border-radius: 4px;
    font-size: 13px;
    color: var(--acc);
    cursor: pointer;
    font-family: inherit;
}

.watch-new-list-row:hover[b-xqz9756bsy] {
    background: var(--hov);
}

.watch-new-list-form[b-xqz9756bsy] {
    display: flex;
    align-items: center;
    gap: 6px;
}

.watch-new-list-input[b-xqz9756bsy] {
    flex: 1;
    min-width: 0;
    box-sizing: border-box;
    height: 28px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--inbg);
    color: var(--ink);
    padding: 0 8px;
    font-size: 13px;
    font-family: inherit;
}

.watch-new-list-add[b-xqz9756bsy],
.watch-new-list-cancel[b-xqz9756bsy] {
    flex-shrink: 0;
    height: 28px;
    padding: 0 9px;
    border-radius: 6px;
    border: 1px solid var(--line);
    background: var(--card);
    color: var(--ink);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
}

.watch-new-list-add:disabled[b-xqz9756bsy],
.watch-new-list-cancel:disabled[b-xqz9756bsy] {
    opacity: .6;
    cursor: default;
}

/* The app-wide error look (app.css, D-213): --neg text and a 3px --neg left rule, no glyph.
   Written here rather than picked up from app.css because a scoped `.watch-…[b-…]` outranks the
   global rule. `.watch-status` carries only failure copy ("Couldn't save — try again", the sole
   value assigned to it), so it is an error surface and takes the same rule. */
.watch-new-list-error[b-xqz9756bsy] {
    font-size: 12px;
    color: var(--neg);
    border-left: 3px solid var(--neg);
    padding-left: 8px;
}

.watch-status[b-xqz9756bsy] {
    font-size: 12px;
    color: var(--neg);
    border-top: 1px solid var(--line4);
    border-left: 3px solid var(--neg);
    padding-top: 6px;
    padding-left: 8px;
}

/* Fix round 1, Important 6 (owner ruling, provisional -- Task 25 records it in card.md):
   the honest-minimum failure state for a load that never resolved -- reuses .watch-status's
   own failure-copy dress rather than inventing new vocabulary. */
.watch-load-error[b-xqz9756bsy] {
    font-size: 12.5px;
    color: var(--neg);
    border-left: 3px solid var(--neg);
    padding: 6px 4px 6px 8px;
}
/* /Components/Catalog/BinderGrid.razor.rz.scp.css */
.binder-grid[b-a3yqshnpyr] { display: grid; gap: 12px; }

/* The tile's box, and the two elements that can BE it: the anchor, when the tile is nothing but
   its link, and the wrapper, when a footer sits beside that link (.has-footer, below). One rule
   rather than two copies -- the copies were identical the day they were written and nothing would
   have said so once one of them moved. Each selector keeps only what is its own: the anchor's
   link reset, the wrapper's column. */
.bg-tile[b-a3yqshnpyr], .bg-tilewrap.has-footer[b-a3yqshnpyr] {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 10px;
    transition: box-shadow 0.15s;
}
.bg-tile[b-a3yqshnpyr] { display: block; text-decoration: none; color: inherit; }
.bg-tile:hover[b-a3yqshnpyr], .bg-tilewrap.has-footer:hover[b-a3yqshnpyr] { box-shadow: 0 6px 20px rgba(20, 19, 26, 0.10); }
.bg-art[b-a3yqshnpyr] {
    aspect-ratio: 325 / 450;
    border-radius: 5px;
    overflow: hidden;
}
.bg-art img[b-a3yqshnpyr] { width: 100%; height: 100%; object-fit: cover; display: block; }
.bg-body[b-a3yqshnpyr] { margin-top: 8px; }

/* TileActions (2026-08-28) -- only present when a caller supplies the slot, so this never
   reaches SetPage/CharacterPage's tiles. The wrapper carries no layout properties of its
   own beyond position: relative, so it sizes to the grid cell exactly as .bg-tile did. */
.bg-tilewrap[b-a3yqshnpyr] { position: relative; }
.bg-tile-actions[b-a3yqshnpyr] { position: absolute; top: 8px; right: 8px; z-index: 1; }

/* A tile with a footer (D-284 slice 4): the footer holds links, which may not sit inside the
   tile's own anchor, so it is a sibling of it -- and the two have to keep reading as ONE card. The
   wrapper is the box (shared rule at the top of this file) and the anchor gives its own up, so the
   tile looks identical and the footer sits inside the border rather than under it. Only the callers
   that pass TileFooter reach these rules; nothing else on the page can see .has-footer. */
.bg-tilewrap.has-footer[b-a3yqshnpyr] { display: flex; flex-direction: column; }
.bg-tilewrap.has-footer > .bg-tile[b-a3yqshnpyr] { background: none; border: 0; border-radius: 0; padding: 0; }
.bg-tilewrap.has-footer > .bg-tile:hover[b-a3yqshnpyr] { box-shadow: none; }

/* The link has to keep covering the tile. It stopped: with the footer outside the anchor, 59.00px
   of a 418.27px tile at a 1480px viewport -- 14%, and the strip carrying the price, which is what
   a gallery is scanned for -- fell outside the click target, 36.50px of it on a tile with no chip.
   A stretched ::after puts the target back over the whole card, against the wrapper's own
   position: relative. Anything inside the footer that must stay hoverable or clickable in its own
   right raises itself above this layer on z-index 1, which is the layer .bg-tile-actions has used
   for the same reason since 2026-08-28. */
.bg-tilewrap.has-footer > .bg-tile[b-a3yqshnpyr]::after { content: ''; position: absolute; inset: 0; }

/* An exited holding's tile, ruled 2026-09-18 off the rework (Cardstock Binder.rework-2026-09-15.html:582,
   `gBd: … h.exited ? negBg(0.5) : line`): the table edges an exited row and the gallery has to say the
   same thing at a glance, or the two views disagree about what the user is looking at. --neg2 is the
   token the table's own edge takes (HoldingsTable.razor.css's .h-row.is-signal) -- a fill hue, legal as a border and
   never as text -- and it recolours the 1px border the tile already declares, so the cue costs no
   layout and an unexited tile needs no placeholder to stay the same size. The class itself is the
   caller's (TileClass): this file knows nothing about screens or exits. */
.bg-tilewrap.has-footer.is-signal[b-a3yqshnpyr] { border-color: var(--neg2); }
/* .bg-body's own gap over the art, so the footer's first line sits off the body the way the body
   sits off the art -- and `auto` on top of it, which pins the footer to the bottom of a stretched
   tile. Rendered both ways at the real tile widths: a footer whose rows vary (the Binder's
   source-screen chip is absent on a position that names no screen) leaves the money row of those
   tiles a whole line higher than their neighbours', and the value is the thing a gallery is
   scanned for. Pinned to the bottom, every money row in a grid row lines up and the variable
   line takes the slack above it instead. */
.bg-tile-footer[b-a3yqshnpyr] { margin-top: auto; padding-top: 8px; }
/* /Components/Catalog/CardCell.razor.rz.scp.css */
/* The Home band's card column, lifted (app.css .band-row / .band-cell-thumb / .band-card-name /
   .band-card-set). The 6px gap is the band's own column-gap between its thumb track and its card
   track; 48x66 and the 4px radius are its thumbnail. `min-width: 0` on both the row and the text
   stack is what lets the two lines ellipsise instead of stretching the column. */
.cc-cell[b-8cvfs11suc] {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  text-align: left;
}

/* position: relative is the hover preview's CSS-anchor fallback when nothing could be measured
   (HoverPreview's own doc); flex-shrink keeps the picture at 48px when the name is long. */
.cc-art[b-8cvfs11suc] {
  position: relative;
  flex-shrink: 0;
  width: 48px;
  height: 66px;
  border-radius: 4px;
}

.cc-art img[b-8cvfs11suc] {
  width: 48px;
  height: 66px;
  object-fit: cover;
  border-radius: 4px;
  display: block;
}

.cc-text[b-8cvfs11suc] {
  min-width: 0;
}

/* The `font:` shorthand, not font-weight/font-size alone -- this is what has to beat
   .rt-cell's inherited `font: var(--table-cell-font)` (500 13px JetBrains Mono, app.css) on the Screener, whose
   column key is `card` rather than Browse's `name` and so falls through to that mono default
   (final whole-branch review, Critical 1). Recovered verbatim from the pre-D-236 .sr-name /
   .sr-sub this replaces (RosterTable.razor.css's own `.rt-cell-name` used 500 for Browse;
   unifying on 600 here is deliberate -- "just like the card column on the home watchlist",
   and the Home band's own .band-card-name is 600 (app.css's .band-card-name) -- so Browse's names render
   very slightly bolder than before D-236). */
.cc-name[b-8cvfs11suc] {
  font: 600 14px 'Inter Tight', sans-serif;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cc-sub[b-8cvfs11suc] {
  font: 400 12.5px 'Inter Tight', sans-serif;
  color: var(--mut2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The row that holds the name and whatever badges it carries: D-237's LOW DATA pill (the Screener's
   deleted Screen column, moved beside the name) and, since D-284 slice 4, the HELD badge below.
   Rendered when a host passes EITHER -- SetPage and CharacterPage pass neither, so `.cc-name`'s own
   rule above is untouched there and this row exists only when there is something to put in it.
   `.cc-name` keeps its own ellipsis; a badge is the piece that must never shrink.
   `padding-right` is D-301 row 135 (the owner, on the walk 2026-10-19: "the spacing for the held chip is too
   close to its surroundings"): the roster declares no gutter between columns (RosterTable.razor.css's `.rt-row`)
   and this is the column whose content runs to its track's edge, so with a long name the badge ended at that
   edge and the centred price began 2.5px past it (headless Chrome, the shipped faces, `$1,234.56` beside a
   222px card track). The row stops the same 6px short of the edge that `gap` holds the badge from the name --
   the site's spacing between two text columns (ActivityLog.razor.css's `.al-row > *`) -- so whatever closes the
   row, HELD or LOW DATA, keeps it from the price: 8.5px at that worst case. On the row once, not as a badge
   margin, which would double the gap to a LOW DATA chip after it. */
.cc-name-row[b-8cvfs11suc] {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding-right: 6px;
}

.cc-name-row .cc-name[b-8cvfs11suc] {
  flex: 1 1 auto;
  min-width: 0;
}

/* D-284 slice 4 §4: the Screener's HELD badge. Its shape is the activity mode's own HELD badge
   -- `.al-badge` / `.al-badge.is-held` in Components/Screener/ActivityLog.razor.css -- which is the
   shipped element that already means "you hold this card" (slice 3), so the two say one thing one
   way. Its dress is the Binder's ACTIVE pill's (D-301 row 62, ruled 2026-10-18; HoldingsTable.razor.css's
   `.h-chip.is-active`): `--pos` on the `--posBg` tint with the pill's hairline, so a held card wears one
   colour on the Screener and in the Binder, and the word HELD carries the meaning beside the hue. The
   tint is translucent, so it lies over a `--card` layer of the badge's own and the rendered pair is the
   one ContrastTests pins over `--card` whatever the row beneath is doing (TintOverCardCssTests).
   `flex-shrink: 0` is what keeps the word whole while `.cc-name` beside it ellipsises;
   `cursor: help` is the rework's (:493), matching the tooltip the badge carries.
   `line-height: 1` is D-301 row 135's second fix (the owner, on the walk 2026-10-19: "Yeah, I meant it was too
   close to the stuff below it"). The roster cell's `font` shorthand leaves the badge `line-height: normal`, which
   JetBrains Mono sets at 14px for a 10.5px face, so with the row-62 hairline the badge stood 18px -- a pixel taller
   than the name's own 17px line box -- and it, not the name, set the row's height: its bottom edge met the subtitle's
   line box at 0px, 2.78px above the subtitle's capitals (headless Chrome, the shipped faces, a 222px card track).
   At `line-height: 1` the word is all capitals, so nothing is clipped, the padding and hairline are unchanged, and
   the badge stands 14.5px, centred in the name's line box: 1.25px clear of the subtitle's line box and 4.03px clear
   of its capitals, above the bottom of the name's own descenders. The name row is 17px again, the height of a
   bare `.cc-name`, so a held row's name and subtitle sit exactly where an unheld row's do. */
.cc-badge[b-8cvfs11suc] {
  flex-shrink: 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: 10.5px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.04em;
  border-radius: 3px;
  padding: 1px 5px;
  white-space: nowrap;
  color: var(--pos);
  background: linear-gradient(var(--posBg), var(--posBg)), var(--card);
  border: 1px solid color-mix(in srgb, var(--pos) 30%, transparent);
  cursor: help;
}

/* D-290: a sub-line led by host markup (SubPrefix) — the backtest's tier chip and re-entry marker. The host's
   own scoped rules keep the prefix at its width; the text beside it is what ellipsises. */
.cc-sub-row[b-8cvfs11suc] {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.cc-sub-row > .cc-sub-text[b-8cvfs11suc] {
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* /Components/Catalog/DeferredIndexBlock.razor.rz.scp.css */
.dib[b-9v75n8ynar] { width: 220px; }
.dib-chart[b-9v75n8ynar] {
    height: 52px;
    border: 1px dashed var(--line3);
    border-radius: 4px;
    background: var(--mutbg);
}
.dib-caption[b-9v75n8ynar] {
    margin-top: 4px;
    font: 500 11.5px 'JetBrains Mono', monospace;
    color: var(--mut2);
}
.dib-deltas[b-9v75n8ynar] { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; margin-top: 6px; }
.dib-delta-label[b-9v75n8ynar] { font: 500 11px 'JetBrains Mono', monospace; color: var(--mut2); margin-right: 6px; }
.dib-delta-value[b-9v75n8ynar] { font: 700 15px 'JetBrains Mono', monospace; color: var(--mut2); }
/* /Components/Catalog/DensityToggle.razor.rz.scp.css */
.density-toggle[b-0afqx2o9ve] {
    display: inline-flex;
    border: 1px solid var(--line);
    border-radius: 6px;
    overflow: hidden;
}
.density-toggle button[b-0afqx2o9ve] {
    height: 28px;
    padding: 0 12px;
    border: 0;
    background: var(--card);
    color: var(--mut);
    font: 600 13px 'JetBrains Mono', monospace;
    text-transform: lowercase;
    cursor: pointer;
}
.density-toggle button.active[b-0afqx2o9ve] {
    background: var(--acc);
    color: var(--card);
}
/* /Components/Catalog/FilterPopover.razor.rz.scp.css */
.pf-overlay[b-6ixop2h210] { position: fixed; inset: 0; z-index: 40; }
.pf-pop[b-6ixop2h210] { position: absolute; top: 31px; z-index: 50; width: 300px; max-height: 380px;
    overflow-y: auto; background: var(--card); border: 1px solid var(--line);
    border-radius: 8px; box-shadow: 0 10px 30px rgba(20, 19, 26, 0.15); padding: 8px; }
.pf-section[b-6ixop2h210] { font: 600 11px 'Inter Tight', sans-serif; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--mut2); padding: 4px 8px; }
.pf-attr[b-6ixop2h210] { display: flex; justify-content: space-between; width: 100%; border: 0;
    background: none; padding: 7px 8px; font-size: 13px; color: var(--ink);
    cursor: pointer; border-radius: 5px; }
.pf-attr:hover[b-6ixop2h210] { background: var(--mutbg); }
.pf-editor-head[b-6ixop2h210] { display: flex; align-items: center; gap: 6px; font-weight: 600;
    padding: 4px 8px; }
.pf-back[b-6ixop2h210] { border: 0; background: none; font-size: 16px; cursor: pointer; color: var(--mut); }
.pf-note[b-6ixop2h210] { font-size: 12px; color: var(--mut2); padding: 0 8px; margin: 2px 0 6px; }
.pf-options[b-6ixop2h210] { max-height: 220px; overflow-y: auto; }
.pf-option[b-6ixop2h210] { display: flex; align-items: center; gap: 8px; width: 100%; border: 0;
    background: none; padding: 6px 8px; font-size: 13px; cursor: pointer;
    border-radius: 5px; color: var(--ink); }
.pf-option:hover[b-6ixop2h210] { background: var(--mutbg); }
.pf-box[b-6ixop2h210] { width: 14px; height: 14px; border: 1px solid var(--line3); border-radius: 3px;
    display: grid; place-items: center; font-size: 10px; }
.pf-option.on .pf-box[b-6ixop2h210] { background: var(--acc); border-color: var(--acc); color: var(--card); }
.pf-footer[b-6ixop2h210] { display: flex; align-items: center; justify-content: space-between;
    padding: 8px 8px 4px; border-top: 1px solid var(--line4); margin-top: 6px; }
.pf-preview[b-6ixop2h210] { font-size: 12px; color: var(--mut2); }
.pf-add[b-6ixop2h210] { border: 0; border-radius: 6px; padding: 5px 14px; font: 600 12.5px
    'Inter Tight', sans-serif; background: var(--acc); color: var(--card); cursor: pointer; }
.pf-add:disabled[b-6ixop2h210] { background: var(--mutbg); color: var(--mut2); cursor: not-allowed; }
/* /Components/Catalog/PendingGlyph.razor.rz.scp.css */
/* JetBrains Mono explicitly, whatever the parent is set in: Inter has no U+25CC, and
   the system fallback draws a wide, faint ring that reads as a plain circle. Glyphs are
   mono app-wide (TierStrip.razor.css, D-092; D-198 UAT finding 2). */
.gate-glyph[b-dmr8t4ee7t] {
    color: var(--mut2);
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    margin-left: 4px;
    cursor: help;
}
.gate-glyph:focus-visible[b-dmr8t4ee7t] {
    outline: 2px solid var(--acc);
    outline-offset: 1px;
}
/* /Components/Catalog/RosterTable.razor.rz.scp.css */
.roster-table[b-tpho6gdqnl] {
    /* D-298 UAT walk 2026-10-08, finding 5: the last row's bottom corners. 9px is the padding box's own radius — the 10px edge less its
       1px border — so a row painted to the edge follows the curve inside it. A weld that squares the table's foot (the
       pager's, app.css's .roster-paged; the Screener's hidden-rows banner) sets this to 0.
       Ruling R-j (D-298 UAT walk 2026-10-08, finding 5, "all the corners"), as the three ledgers apply it: the last row's own
       rule sits 1px above this box's bottom border and only doubles it, so by default the last row drops it. The same two
       welds keep it -- there the table has no bottom border, and the row's rule is the one that parts it from the pager or
       the banner -- by setting this to .rt-row's own rule. */
    --rt-foot-radius: 9px;
    --rt-foot-rule: none;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    /* Clip the X axis ONLY. Any scroll-container overflow value would break the §4.7
       sticky header, and two-axis `overflow: clip` broke Blazor's Virtualize — it took
       this box for the scroll container and stopped hearing page scroll, wedging the
       roster at ~106 rendered rows (owner UAT find, 2026-08-19; D-118 correction).
       The Y axis must stay visible: the page is the one true scroller. On a viewport
       narrower than the columns the table clips instead of scrolling — the mockup had
       no horizontal scroll either. */
    overflow-x: clip;
}
.rt-head[b-tpho6gdqnl], .rt-row[b-tpho6gdqnl] { display: grid; align-items: center; padding: 6px 16px; }
/* shared-components.md §4.7's ruled stack: the table header pins below the 48px nav
   (z-index 10 under the nav's 20) so the sort/resize controls never scroll away. The
   solid background masks rows passing beneath. It wears the app's table face (app.css's
   --table-head-*, D-298 UAT walk 2026-10-08, finding 2).
   D-298 UAT walk 2026-10-08, finding 5: the X-only clip above never clips this box to the table's curve, so its own background
   squared the two top corners off it (the owner's screenshot). The padding box's radius puts them back on it. */
.rt-head[b-tpho6gdqnl] {
    position: sticky;
    top: 48px;
    z-index: 10;
    background: var(--card);
    border-bottom: 1px solid var(--line);
    border-radius: 9px 9px 0 0;
    font: var(--table-head-font);
    letter-spacing: var(--table-head-tracking);
    text-transform: uppercase;
    color: var(--mut2);
}
.rt-head-cell[b-tpho6gdqnl] { position: relative; text-align: center; user-select: none; }
.rt-head-cell.sortable[b-tpho6gdqnl] { cursor: pointer; }
.rt-head-cell.sortable:hover[b-tpho6gdqnl] { color: var(--ink); }
.rt-grip[b-tpho6gdqnl] {
    position: absolute;
    right: 0;
    top: 0;
    cursor: col-resize;
    color: var(--line3);
    touch-action: none;
}
.rt-row[b-tpho6gdqnl] { border-bottom: 1px solid var(--line4); }
/* task-17: with RowHref the row IS the link — middle-click, right-click, open-in-new-tab
   and the keyboard all work, which no @onclick handler gives. It must be indistinguishable
   from the div it replaces, so the anchor's own two defaults are undone; the focus ring
   stays, because a link the keyboard can reach has to show where it is. */
a.rt-row[b-tpho6gdqnl] { text-decoration: none; color: inherit; }
/* D-298 UAT walk 2026-10-08, finding 5: the last row's own corners, for the same reason as the header's — the row's own
   rule otherwise squares the foot's two corners. With RowActions the row is split in two, so each half takes its own
   corner -- and drops its own half of the rule (--rt-foot-rule, above). */
.rt-body > .rt-row:last-child[b-tpho6gdqnl] {
    border-radius: 0 0 var(--rt-foot-radius) var(--rt-foot-radius);
    border-bottom: var(--rt-foot-rule);
}
.rt-body > .rt-rowwrap:last-child > .rt-row[b-tpho6gdqnl] {
    border-bottom-left-radius: var(--rt-foot-radius);
    border-bottom: var(--rt-foot-rule);
}
.rt-body > .rt-rowwrap:last-child > .rt-act[b-tpho6gdqnl] {
    border-bottom-right-radius: var(--rt-foot-radius);
    border-bottom: var(--rt-foot-rule);
}
.rt-cell[b-tpho6gdqnl] { text-align: center; font: var(--table-cell-font); color: var(--mut); }
.rt-cell-name[b-tpho6gdqnl] { font: 500 14px 'Inter Tight', sans-serif; color: var(--ink); }

/* RowActions (D-284 slice 4) — reached only by a caller that supplies the slot, so nothing here
   can touch Browse, Set, Character or the two binder tables. The wrapper carries the row's full
   width and splits it; the row keeps its own tracks inside the first half.

   No `align-items` on the wrapper, deliberately: the default stretch is what makes .rt-act as tall
   as the row, which is what lets it carry the second half of the row's 1px rule. With the rule left
   on .rt-row alone it would stop where the action track begins — ActionWidth short of the table's
   edge — and centring the action box would stop it too. So the two halves are drawn by the two elements,
   at the same height, and the row's pitch is exactly what it was: .rt-row still owns its own border
   inside its own declared height. */
.rt-rowwrap[b-tpho6gdqnl] { display: grid; }
.rt-rowwrap > .rt-act[b-tpho6gdqnl] {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-right: 16px;                        /* .rt-head/.rt-row's own gutter, above */
    border-bottom: 1px solid var(--line4);      /* the rest of .rt-row's rule */
}
/* /Components/Catalog/ShelfHeader.razor.rz.scp.css */
.shelf-header[b-nfcfgzjfi3] { display: flex; align-items: baseline; gap: 10px; margin: 28px 0 0; }
.shelf-title[b-nfcfgzjfi3] { font: 600 18px 'Inter Tight', sans-serif; color: var(--ink); margin: 0; }
.shelf-meta[b-nfcfgzjfi3] { font: 400 12px 'JetBrains Mono', monospace; color: var(--mut2);
    white-space: nowrap; }
.shelf-meta + .shelf-meta[b-nfcfgzjfi3]::before { content: "· "; }
/* The rule is what separates the shelves; baseline-aligned flex needs it re-centred. */
.shelf-rule[b-nfcfgzjfi3] { flex: 1; height: 1px; background: var(--line); align-self: center; }
/* /Components/Catalog/SortMenu.razor.rz.scp.css */
.sort-menu[b-g57if7j0oo] { display: inline-flex; align-items: center; gap: 6px; }
.sort-label[b-g57if7j0oo] { font: 500 12.5px 'JetBrains Mono', monospace; color: var(--mut2); }
.sm-anchor[b-g57if7j0oo] { position: relative; }
.sm-select[b-g57if7j0oo] { height: 28px; display: inline-flex; align-items: center; gap: 10px; padding: 0 8px 0 10px;
    border: 1px solid var(--line); border-radius: 6px; background: var(--inbg); color: var(--ink);
    font: 600 13px 'JetBrains Mono', monospace; cursor: pointer; }
.sm-select[aria-expanded="true"][b-g57if7j0oo] { border-color: var(--acc); }
.sm-chev[b-g57if7j0oo] { color: var(--mut2); font-size: 11px; }
.sm-dir[b-g57if7j0oo] { width: 28px; height: 28px; display: inline-grid; place-items: center; border: 1px solid var(--line);
    border-radius: 6px; background: var(--card); color: var(--mut); font: 600 12px 'JetBrains Mono', monospace;
    cursor: pointer; }
.sm-overlay[b-g57if7j0oo] { position: fixed; inset: 0; z-index: 40; }
.sm-list[b-g57if7j0oo] { position: absolute; top: 34px; right: 0; z-index: 50; width: 220px; box-sizing: border-box;
    background: var(--card); border: 1px solid var(--line); border-radius: 8px;
    box-shadow: 0 10px 30px rgba(20, 19, 26, 0.15); padding: 6px; }
.sm-item[b-g57if7j0oo] { display: flex; justify-content: space-between; align-items: center; width: 100%; padding: 7px 8px;
    border: 0; background: none; border-radius: 5px; color: var(--ink);
    font: var(--table-cell-font); cursor: pointer; text-align: left; }
.sm-item:hover[b-g57if7j0oo] { background: var(--mutbg); }
.sm-item.on[b-g57if7j0oo] { background: var(--accBg); color: var(--acc); font-weight: 700; }
.sm-item.on[b-g57if7j0oo]::after { content: "✓"; color: var(--acc); }
.sm-item.deferred[b-g57if7j0oo] { color: var(--mut2); cursor: not-allowed; }
.sm-glyph[b-g57if7j0oo] { color: var(--mut2); }
/* /Components/Catalog/SortPills.razor.rz.scp.css */
.sort-pills[b-rw2x6lpsb8] { display: inline-flex; align-items: center; gap: 6px; }
.sort-label[b-rw2x6lpsb8] { font: 500 12.5px 'JetBrains Mono', monospace; color: var(--mut2); }
.pill[b-rw2x6lpsb8] {
    height: 24px;
    padding: 0 10px;
    border: 1px solid var(--line);
    border-radius: 99px;
    background: var(--card);
    color: var(--mut);
    font: 600 12px 'JetBrains Mono', monospace;
    cursor: pointer;
}
.pill.active[b-rw2x6lpsb8] { background: var(--acc); border-color: var(--acc); color: var(--card); }
.pill.deferred[b-rw2x6lpsb8] { color: var(--mut2); cursor: not-allowed; }
/* /Components/Field.razor.rz.scp.css */
/* The label, input and helper rules themselves are app.css's — global chrome shared with the
   three Settings cards, guarded by SharedChromeTests. What lives here is only what this
   component's own markup owns.

   account.md:45-48 — the label row that shares its line with a trailing control ("Forgot?").
   This moved out of AccountPage.razor.css when the component started rendering it: Blazor
   scopes a rule to the component that authors the markup, so a page rule can no longer reach
   a label a child component emits.

   Per-surface spacing stays with the surface and reaches in through ::deep — see
   AccountPage.razor.css. */

.field-row[b-cydvsrlim5] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
}

/* Inside the row the label carries no margin of its own; the row positions it. */
.field-row .field-label[b-cydvsrlim5] {
    margin: 0;
}
/* /Components/FilterChip.razor.rz.scp.css */
/* The prototypes' chip, verbatim except for one hue — see the ✕ hover below.
   `Cardstock Screener.dc.html:177` and `Cardstock Browse.dc.html:102` draw the same inline
   style on both screens; D-228 ruling 2 makes it the app's one chip.

   The prototypes hard-code the border as rgba(74, 99, 208, 0.25), which is light --acc at 25%
   and therefore stays blue-violet in dark mode. color-mix() over the token gives the same
   light value and a border that actually follows the theme. */
.filter-chip[b-hgvkm3j27j] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--accBg);
    border: 1px solid color-mix(in srgb, var(--acc) 25%, transparent);
    color: var(--ink);
    border-radius: 6px;
    padding: 3px 6px 3px 9px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12.5px;
    font-weight: 500;
}

/* R15, D-290 §7: the read-only chip in backtest mode — the rework mockup's `:195` look. --mut on --mutbg is
   ContrastTests' row; --line is a border, not text. */
.filter-chip.is-locked[b-hgvkm3j27j] {
    background: var(--mutbg);
    border-color: var(--line);
    color: var(--mut);
    padding: 3px 9px;
    cursor: default;
}

.chip-remove[b-hgvkm3j27j] {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--mut);
    font-size: 12px;
    padding: 0 2px;
    line-height: 1;
}

/* The one departure from the prototypes, and it is the departure CLAUDE.md predicts: colour
   has outpaced them. Both prototypes hover the ✕ to --neg2, which is a FILL hue — the
   2026-08-22 item-5 sweep (brand.md §8) re-routed nine text declarations off it onto --neg for
   exactly this reason, and ContrastTests.A_state_fill_hue_is_never_used_as_text makes a tenth
   unshippable. It is a live AA failure here rather than a stylistic point: --neg2 on --accBg
   measures 3.88:1 light and 3.43:1 light-CVD, under D-145's binding 4.5:1. --neg clears in all
   four modes (4.73 / 4.94 / 4.74 / 6.04) and is pinned by
   ContrastTests.The_text_hues_clear_AA_on_every_surface. Same hue family, one step darker. */
.chip-remove:hover[b-hgvkm3j27j] {
    color: var(--neg);
}
/* /Components/Home/ActivityDigest.razor.rz.scp.css */
/* Frame mirrors BinderCard.razor.css's .bc: --card ground, --line border, 8px radius.
   position: relative + an absolutely positioned body is what keeps the rows from sizing this card:
   .ad-body fills whatever the frame leaves below the header, and a response longer than the card clips
   rather than growing the Home row.
   The floor is the fifth row (owner, D-276 UAT round 2), and it amends D-276 ruling 2 — this card does
   not take only what the Binder card leaves. 42px of header + ActivityDigest.Rows × 60px of row + 45px
   of more-line + this frame's own 2px of border = 389px, which box-sizing makes the outer height. It is
   the same 389 the deleted feed declared, with the more-line in the slot its pager held.
   .home-grid is align-items: stretch, so the Binder card beside it grows to the same 389 and its
   justify-content: space-between spreads the extra between its blocks. ActivityDigestTests reads every
   number above out of this file and recomputes the sum. */
.ad[b-0th15vw8tl] { position: relative; background: var(--card); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; box-sizing: border-box; min-height: 389px; }
.ad-head[b-0th15vw8tl] { box-sizing: border-box; height: 42px; display: flex; justify-content: space-between; align-items: baseline; gap: 10px; padding: 10px 12px; border-bottom: 1px solid var(--line); }
.ad-title[b-0th15vw8tl] { font: 700 17.5px 'Inter Tight', sans-serif; color: var(--ink); margin: 0; flex-shrink: 0; }
/* The header's sentence outruns its half of the bar at every width this card is drawn at: measured in
   headless Chrome against these stylesheets, the three-clause worst case wants 742px and the bar offers
   610 at a 1280 viewport and 713 at 1920. So the ellipsis goes on the sentence and not on .ad-meta
   around it — with it on the container, the "your screens →" link on the end is what the ellipsis eats,
   and that link is the card's only way into the Screener. The sentence gives way; the link never does. */
.ad-meta[b-0th15vw8tl] { display: flex; align-items: baseline; min-width: 0; font-size: 12.5px; color: var(--mut2); white-space: nowrap; }
.ad-clauses[b-0th15vw8tl] { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The margin is what separates the sentence from the link, because the spaces inside the span cannot:
   .ad-meta is a flex container, so this span is blockified and the leading and trailing whitespace of
   " · " is stripped at its own edges. Measured in headless Chrome against the shipped stylesheets, the
   dot rendered welded to both neighbours — 3.61px wide with a 0px gap on either side. 4px each side, and
   the bar has the room: the one-clause header measures 398.63px in a 640.03px bar at a 1280 viewport, so
   the link still renders whole. ActivityDigestTests reads this declaration. */
.ad-sep[b-0th15vw8tl] { flex-shrink: 0; margin: 0 4px; }
.ad-link[b-0th15vw8tl] { font-size: 12.5px; color: var(--link); text-decoration: none; flex-shrink: 0; }
/* Nothing inside this card scrolls (D-276 UAT round 1, finding 2): with the cursor over it, a wheel
   scrolled the list instead of the page. The feed answered that with a pager; the digest answers it by
   being one card's worth of rows, and overflow: hidden is the backstop — a response longer than the card
   clips rather than quietly growing a scrollbar back. */
.ad-body[b-0th15vw8tl] { position: absolute; top: 42px; left: 0; right: 0; bottom: 0; overflow: hidden; display: flex; flex-direction: column; }
/* The app-wide error look (app.css, D-213), restated because a scoped rule outranks the global one. */
.ad-error[b-0th15vw8tl] { margin: 8px 12px; font-size: 13.5px; color: var(--neg); border-left: 3px solid var(--neg); padding-left: 8px; }
.ad-empty[b-0th15vw8tl] { padding: 12px; }
.ad-empty p[b-0th15vw8tl] { margin: 0 0 4px; font-size: 13.5px; color: var(--mut); }
.ad-empty a[b-0th15vw8tl] { font-size: 13px; color: var(--link); }
/* Mockup rows (Cardstock Home.rework-2026-09-15.html:194-202): glyph column 12px mono, gap 10px,
   padding 8px 12px 8px 9px over a 3px edge, --line4 divider, hover fill.
   The height is declared, not left to the content: .ad's floor is Rows of these plus the more-line, with
   nothing spare, so a row that grew would push the last one out of the frame. box-sizing makes 60px the
   whole row, padding, edge and rule included; flex: 0 0 auto keeps five rows at 60 rather than squeezing
   six into the same 300px; and the overflow is the backstop for anything inside that still cannot fit. */
.ad-row[b-0th15vw8tl] { display: flex; gap: 10px; align-items: baseline; height: 60px; box-sizing: border-box; flex: 0 0 auto; overflow: hidden; padding: 8px 12px 8px 9px; border-left: 3px solid transparent; border-bottom: 1px solid var(--line4); color: inherit; text-decoration: none; cursor: pointer; }
.ad-row:hover[b-0th15vw8tl], .ad-row:focus-visible[b-0th15vw8tl] { background: var(--hov); }
/* The personal sell signal's edge, and the same token ActivityLog.razor.css's .al-row.is-personal gives that row in the
   Screener's log: --neg2 is a fill hue, legal as a border and never as text (ContrastTests'
   A_state_fill_hue_is_never_used_as_text). Every row declares the edge, transparent where there is nothing
   to say, so lighting it up moves no content: the 3px is inside the row's border box on every row, and a
   pinned row is not 3px narrower than a screen row. It does NOT align the two rows' names — a pinned row's
   12px glyph column and its 10px gap put .ad-name 22px further right, measured. */
.ad-row.is-signal[b-0th15vw8tl] { border-left-color: var(--neg2); }
/* The named face first, as every other glyph span in the app sets it (.al-glyph, .sig-glyph, .chip) and as
   the mockup sets this one (:195). Leading with ui-monospace draws ▼ in SF Mono on macOS — the drift
   D-198's UAT finding 2 named, and D-276 UAT round 1 finding 3 on the feed this card replaced.
   GateGlyphCssTests. */
.ad-glyph[b-0th15vw8tl] { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; width: 12px; flex-shrink: 0; color: var(--neg); }
.ad-main[b-0th15vw8tl] { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
/* One line, never two: it carries the name AND what happened, and each can be long on its own — a screen
   name runs to 80 characters (ScreenerService.MaxNameLength) and card names carry their variant in
   parentheses. The name is the half that gives way, on SearchBox .sb-name's four declarations — min-width: 0
   to let it shrink, then the ellipsis — with the whole of it on the title. */
.ad-line1[b-0th15vw8tl] { display: flex; gap: 6px; align-items: baseline; flex-wrap: nowrap; }
.ad-name[b-0th15vw8tl] { font-weight: 500; color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* HELD n · SELL SIGNAL, the badge ActivityLog.razor.css's .al-badge.is-sell already draws for this fact:
   --neg on the negative tint, a pair ContrastTests pins over --card (Text_on_the_translucent_warn_fill_clears_AA).
   The tint is translucent and this row hovers to --hov, so it is laid over a --card layer of its own and the
   rendered pair is the pinned one on either background (TintOverCardCssTests holds the layer).
   The mockup's own badge (:199) is --neg3 on --negBg07; production shipped this pair for the same badge one
   slice ago, and CLAUDE.md makes production the authority for which token a thing takes. */
.ad-badge[b-0th15vw8tl] { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; border-radius: 3px; padding: 1px 5px; white-space: nowrap; flex-shrink: 0; color: var(--neg); background: linear-gradient(var(--negBg), var(--negBg)), var(--card); }
/* ▲n ▼n ◆n, mono at the mockup's 13px (:200). One hue for the whole string, and a neutral one: the three
   glyphs are what say which direction each count is, and the rail draws the same counts as three separately
   hued spans only because it has no sentence beside them (ScreenRail.razor). GateGlyphCssTests. */
.ad-counts[b-0th15vw8tl] { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: var(--mut); flex-shrink: 0; }
/* The pinned row's What line. Mono because it can carry ◆ — "Left in the rules change ◆ — …", the pulled-out
   wording ActivityLogComposer.Lines produces — which the Screener's log draws in the mono face too
   (.al-what-line), and the mockup sets this span in it (:200). GateGlyphCssTests.
   It neither wraps nor shrinks: this line names the rule that failed, and it is the row's state in words
   (brand §4 — the ▼ and the hue never carry it alone), so the name beside it is the half that gives way.
   Measured in headless Chrome, the longest wording this composes — a pulled-out row's "Left in the rules
   change ◆ — <rule> failed" — renders whole at 1280, 1440, 1512 and 1920 with the name ellipsised to make
   room. Longer still and the row's own overflow cuts it, which is why it carries the whole line on hover,
   as the Screener's log gives the same sentence (.al-what-line, ActivityLog.razor:218). */
.ad-rule[b-0th15vw8tl] { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; color: var(--neg); white-space: nowrap; flex-shrink: 0; }
/* Evidence and the reader's own position, on one line that ellipsises: the row is a fixed 60px and this
   sentence is unbounded — a pulled-out row's detail runs to 130 characters — so it is truncated rather than
   wrapped into a third line the row has no height for, and carries the whole of itself on hover as the
   log's own detail line does (.al-detail, ActivityLog.razor:221). */
.ad-ev[b-0th15vw8tl] { font-size: 13px; color: var(--mut); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-when[b-0th15vw8tl] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12px; color: var(--mut2); flex-shrink: 0; }
/* The screens the card had no room for, in the slot the pager held: 45px, welded to the card's bottom edge
   by the same rule the pager drew above itself (Pager.razor.css's .pg-bar border-top), with the card's radius
   clipping the corners. box-sizing puts the border inside the 45, which is what the floor's arithmetic counts. */
.ad-more[b-0th15vw8tl] { display: flex; align-items: center; justify-content: center; height: 45px; box-sizing: border-box; flex: 0 0 auto; border-top: 1px solid var(--line); font-size: 13px; color: var(--link); text-decoration: none; }
.ad-more:hover[b-0th15vw8tl], .ad-more:focus-visible[b-0th15vw8tl] { background: var(--hov); }
/* /Components/Home/BinderCard.razor.rz.scp.css */
.bc[b-cuamwsx6ew] { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 12px; display: flex; flex-direction: column; justify-content: space-between; }
.bc-head[b-cuamwsx6ew] { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; }
.bc-title[b-cuamwsx6ew] { font: 700 17.5px 'Inter Tight', sans-serif; color: var(--ink); margin: 0; }
.bc-link[b-cuamwsx6ew] { font-size: 12.5px; color: var(--link); text-decoration: none; }
/* The app-wide error look (app.css, D-213): --neg text and a 3px --neg left rule, no glyph.
   Written here because a scoped `.bc-error[b-…]` outranks the global rule. It stood in one rule
   with `.bc-empty p` until D-276 UAT round 2 deleted that block: the empty state is now the
   populated card's own slots with dashes in them, so it needs no muted sentence of its own. */
/* The auto bottom margin is load-bearing since the digest's floor (D-276 UAT round 2, where the
   deleted feed declared the same 389 — ActivityDigest.razor.css): this line is
   the card's second and last flex child, and space-between would otherwise drop it to the bottom
   edge — 389px of card, less 26px of padding and border, less a 22.75px header and this 20.25px
   line, is 320px of gap. That is the chin the owner ruled against, on the one arm nobody walks. The
   auto margin takes that free space instead, so the sentence sits under the header, 10px below it,
   where the grids start in the other arms. */
.bc-error[b-cuamwsx6ew] { margin: 0 0 auto; font-size: 13.5px; color: var(--neg); border-left: 3px solid var(--neg); padding-left: 8px; }
.bc-r3[b-cuamwsx6ew] { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
.bc-r3-2[b-cuamwsx6ew], .bc-sup[b-cuamwsx6ew] { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line4); }
.bc-k[b-cuamwsx6ew] { font-size: 12.5px; color: var(--mut2); }
.bc-v1[b-cuamwsx6ew] { font-family: "JetBrains Mono", Menlo, monospace; font-size: 25.5px; font-weight: 700; color: var(--ink); }
.bc-pp[b-cuamwsx6ew] { font-size: 14px; }
.bc-sub[b-cuamwsx6ew] { font-family: "JetBrains Mono", Menlo, monospace; font-size: 12.5px; }
.bc-v2[b-cuamwsx6ew] { font-family: "JetBrains Mono", Menlo, monospace; font-size: 15px; font-weight: 600; color: var(--ink); }
.bc-q[b-cuamwsx6ew] { font-weight: 400; color: var(--mut2); }
/* The sell-signal count on the Positions line (D-284 slice 4 §3). --neg as text, never --neg2
   (ContrastTests.A_state_fill_hue_is_never_used_as_text).
   The face is named because of inheritance: this span sits inside the .bc-v2 div
   (BinderCard.razor:106-109), and .bc-v2 above led its stack with ui-monospace until D-301 row 11,
   so an unnamed rule here handed that stack to the ▼. D-276 UAT round 1 finding 3 is the same stack on
   another component's glyph span: .af-glyph led its font stack with ui-monospace, "found in review
   rather than on screen" (DECISIONS.md:8035-8036; git show
   c7635dd:src/CardStock.Web/Components/Home/ActivityFeed.razor.css, line 21).
   Measured 2026-09-18. SF Mono carries ▼ ▲ ● and neither ◌ nor ◆
   (/System/Library/Fonts/SFNSMono.ttf, 1314 codepoints, cmap decoded directly). Chrome does not
   implement ui-monospace at all: at 100px it measures identically to a bogus family name (88.92)
   and nothing like monospace/Menlo (60.21), so this ▼ comes out 7.23px there whether the face is
   named here or inherited. That WebKit on
   macOS resolves ui-monospace to SF Mono, which the named stack does not reach, is reasoning and
   not measured — and was the reason D-301 row 11 took the keyword off the front of every stack in
   this sheet. The glyph keeps naming its face (GateGlyphCssTests holds every glyph rule to it). */
/* Its own line, measured: "▼ 2 sell signals" is 115.23px at 12px in this face, and the cell it
   shares with "9 across 6 sets" is a third of the card — 173.19px at a 1480px viewport, 146.53px
   at 1280, 122.53px at 1100 (headless Chrome, the shipped stylesheet). Inline it never fit at any
   width: it wrapped mid-phrase and cost the slot 22.5px of a 15px line. As a block it is one clean
   line costing 18px, down to about a 1050px viewport, below which it wraps the way "across 6 sets"
   already wraps there on its own. */
.bc-signal[b-cuamwsx6ew] { display: block; color: var(--neg); font-family: 'JetBrains Mono', monospace; font-size: 12px; }
/* The card's other four ▲ ▼: the unrealized sub-line's (BinderCard.razor:85), the 30D change's
   (:115) and the two superlatives' (:124-125, through the Superlative fragment). Each sits in a
   slot — .bc-sub and .bc-v2 above, .bc-n > span below — that led its stack with ui-monospace, and
   named no face of its own.
   The face was named on the glyph rather than the slot because a slot-wide font-family was tried
   first and reverted: it moved the money text to JetBrains Mono along with the glyph, leaving
   .bc-30d's digits in a different face from .bc-cost's and .bc-positions's on the same row.
   The deeper fix, every slot not leading with ui-monospace, is D-301 row 11's: the five stacks
   now open with the named face, so the slots and their glyphs draw in one face on every engine,
   and this rule stays as the glyph's own guarantee. */
.bc-glyph[b-cuamwsx6ew] { font-family: 'JetBrains Mono', monospace; }
.bc .pos[b-cuamwsx6ew] { color: var(--pos); }
.bc .neg[b-cuamwsx6ew] { color: var(--neg); }
.bc .mut[b-cuamwsx6ew] { color: var(--mut); }
.bc .mut2[b-cuamwsx6ew] { color: var(--mut2); }
/* The dash-plus-gate values are centred flex rows, as PerformanceTab's (D-198 UAT finding 2). */
.bc .na[b-cuamwsx6ew] { color: var(--mut2); display: flex; align-items: center; }
.bc .na .gate-glyph[b-cuamwsx6ew] { font-weight: 400; }
.bc .acc[b-cuamwsx6ew] { color: var(--acc); }
.bc-sup[b-cuamwsx6ew] { display: flex; flex-direction: column; gap: 4px; }
.bc-sup > div[b-cuamwsx6ew] { display: flex; justify-content: space-between; font-size: 14px; }
.bc-sup .bc-k[b-cuamwsx6ew] { font-size: 14px; }
.bc-n[b-cuamwsx6ew] { font-weight: 500; color: var(--ink); }
/* The child combinator is load-bearing, not tidying: as a descendant selector this also matched the
   .bc-glyph span nested inside the tone span, out-specifying .bc-glyph's own rule — scoped, that is
   .bc-n span[b-…] against .bc-glyph[b-…], one compound more — and handing the glyph back the unnamed
   stack, with a second 4px margin besides. Every span this has ever matched is a direct child, so
   the rendering is unchanged; measured from the built bundle, the glyph computes
   font-family: "JetBrains Mono", monospace and margin-left: 0px inside both slots. */
.bc-n > span[b-cuamwsx6ew] { font-family: "JetBrains Mono", Menlo, monospace; font-weight: 600; margin-left: 4px; }
.bc-legend[b-cuamwsx6ew] { display: flex; gap: 12px; margin-top: 6px; font-size: 12px; color: var(--mut2); font-family: "JetBrains Mono", Menlo, monospace; }
/* The empty arm's link sits on this line but wears the card's link face — the body's Inter at
   12.5px, as .bc-link does in the header — not the legend's 12px mono (owner, D-276 end pass,
   call 1: B). It brings 12.5px onto a 12px row: the line grows 18 → 18.75px, and the card, pinned
   to the Home row's floor, absorbs it in its five gaps at 0.19px each (measured). */
.bc-legend a[b-cuamwsx6ew] { font-family: 'Inter', system-ui, sans-serif; font-size: 12.5px; color: var(--link); text-decoration: none; }
.bc-strip-note[b-cuamwsx6ew] { margin-left: auto; }
[b-cuamwsx6ew] .strip { position: relative; margin-top: 10px; }
[b-cuamwsx6ew] .strip svg { display: block; width: 100%; height: 48px; }
[b-cuamwsx6ew] .strip-fill { fill: var(--accBg); }
[b-cuamwsx6ew] .strip-index { stroke: var(--mut2); stroke-width: 1; stroke-dasharray: 3 3; }
[b-cuamwsx6ew] .strip-binder { stroke: var(--acc); stroke-width: 1.5; }
[b-cuamwsx6ew] .strip-dashed { stroke-dasharray: 3 3; }
[b-cuamwsx6ew] .strip-dotted { stroke-linecap: round; stroke-dasharray: 0.1 3; }
[b-cuamwsx6ew] .strip-hollow { position: absolute; width: 6px; height: 6px; border-radius: 50%; background: var(--card); border: 1.5px solid var(--acc); transform: translate(-50%, -50%); box-sizing: border-box; }
[b-cuamwsx6ew] .strip-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 12px; color: var(--mut2); }
/* /Components/Home/TickerBar.razor.rz.scp.css */
/* Moved from app.css (task-3-brief.md Step 9, 2026-08-28 marketing-and-legal plan): app.css
   is loaded by the WASM shell alone and never by the SSR marketing host, so these rules had
   to leave it or the shared ticker would render unstyled on marketing.

   Corrected 2026-08-28, having been read off a real publish: this file does NOT reach the
   marketing host through CardStock.Api.styles.css, as the line here first claimed.
   CardStock.Web is a Blazor WebAssembly *app*, not a razor class library, so it bundles its
   own scoped CSS into CardStock.Web.styles.css and no consuming app absorbs it --
   CardStock.Api.styles.css aggregates the referenced class libraries only, which today
   means CardStock.Marketing. MarketingHost therefore links CardStock.Web.styles.css too, and
   MarketingStylesheetTests fetches both and asserts .ticker-ghost actually arrives.

   Every comment below is unchanged from app.css except the one line noted inline; they still
   carry D-172 and D-201's reasoning. */

/* D-087 ghost: the market ticker (home.md §3.2/§4.7). Static and deliberately
   motionless -- no `animation` property anywhere in this block -- until the fifteen
   remaining stats are wired (the ticker-aggregates phase). This used to say "until the
   market index exists to drive it", which stopped being true the day the worker first
   ran and D-172 armed the INDEX cell: the index is live, there is just nothing yet for
   a 45s belt to carry past one cell. Same stale-promise shape UAT item 8.4 found in the
   copy next door (TickerBar.razor); owner confirmed the hold 2026-08-28.

   Dressed as chrome, not as a card (2026-08-28). Home.dc.html:56 fills the bar with
   --card, the same token .app-chrome takes, and runs it edge to edge with a bottom rule
   as its only border -- so it reads as the nav continuing, not as a slab under it. It
   filled --mutbg and sat inset, which drew side edges the prototype has nowhere. The
   left gutter is the cap's 20px (Home.dc.html:57), aligning MARKET with the nav's
   logo; the container carries the right gutter only. align-items: stretch runs the
   cap's rule the full 36px, as the prototype does -- center cropped it to text height. */
.ticker-ghost[b-9qhccpyyi5] {
  height: 36px;
  display: flex;
  align-items: stretch;
  padding: 0 20px 0 0;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

.ticker-ghost-cap[b-9qhccpyyi5] {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 0 12px 0 20px;
  border-right: 1px solid var(--line);
}

.ticker-ghost-label[b-9qhccpyyi5] {
  font-size: 12.5px;
  letter-spacing: 0.06em;
  color: var(--mut2);
}

/* Color + not-allowed cursor come from the shared .deferred rule (app.css, global chrome --
   was "this file, above" before the Step 9 move; only ticker-specific sizing lives here). */
.ticker-ghost-cap select[b-9qhccpyyi5] {
  background: transparent;
  border: none;
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  font-weight: 500;
  padding: 2px 0;
}

.ticker-ghost-copy[b-9qhccpyyi5] {
  margin: 0 0 0 20px;
  align-self: center;
  font-size: 13px;
  color: var(--mut);
  /* min-width: 0 is what makes the ellipsis reachable: a flex item defaults to
     min-width: auto and will not shrink below its text, so overflow/text-overflow sat
     inert and the bar clipped the sentence mid-word instead. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Home.dc.html:67 spaces adjacent stats 28px apart; only the first sits 20px off the rule. */
.ticker-cell + .ticker-ghost-copy[b-9qhccpyyi5] {
  margin-left: 28px;
}

/* The ticker's first live cell (D-172): INDEX at 1M. Direction text takes --pos/--neg,
   never --pos2/--neg2 (D-161) -- the production rule every other page already follows. */
.ticker-cell[b-9qhccpyyi5] {
  display: inline-flex;
  /* baseline inside, align-self outside: the prototype renders label/value/suffix as
     inline spans at 11.5/13/11px (Home.dc.html:69), so they share a baseline; the bar's
     align-items: stretch is what needs overriding, not their alignment to each other. */
  align-items: baseline;
  align-self: center;
  gap: 6px;
  margin-left: 20px;
}

.ticker-cell-label[b-9qhccpyyi5] {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11.5px;
  color: var(--mut2);
  letter-spacing: .04em;
}

.ticker-cell-value[b-9qhccpyyi5] {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 700;
}

.ticker-cell-value.pos[b-9qhccpyyi5] {
  color: var(--pos);
}

.ticker-cell-value.neg[b-9qhccpyyi5] {
  color: var(--neg);
}

.ticker-cell-suffix[b-9qhccpyyi5] {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  color: var(--mut2);
}
/* /Components/LoadingRing.razor.rz.scp.css */
/* In flow, centred in whatever contains it — a page, a card, a popover.
   It was `position: fixed; inset: 20vh 0 auto 0`, anchored to index.html's boot ring so the
   boot-fill → fetch-spin handoff never moved a pixel (D-114). Out of flow it occupied no
   space and ignored its container, which is one cause with four faces: Binder's IRON RULE
   slid underneath it (32px), Home's band and Binder card each threw a full-width ring at
   the same viewport coordinates, <main> collapsed so the footer rode up, and WatchlistPicker
   had already hand-rolled a duplicate ring to escape it.
   D-221 amends D-114: the ring now lands near the boot ring rather than exactly on it —
   Binder cannot match at all, since its title and tabs render above — and never on top of
   anything. index.html's boot ring keeps its 20vh anchor and is untouched. */
.loading-ring[b-es4kudw498] { display: flex; flex-direction: column; align-items: center;
    justify-content: center; gap: 10px; padding: 32px 0; flex: 1 0 auto; }
.loading-ring svg[b-es4kudw498] { width: 48px; height: 48px; transform: rotate(-90deg);
    animation: ring-spin-b-es4kudw498 0.9s linear infinite; }
.loading-ring .track[b-es4kudw498] { fill: none; stroke: var(--line); stroke-width: 4px; }
/* SVG dasharray percentages resolve against the square's side, and the r=40% circle's
   circumference is 251.3% of it — so 70.4% draws the 28% arc the spin carries. */
.loading-ring .arc[b-es4kudw498] { fill: none; stroke: var(--acc); stroke-width: 4px;
    stroke-linecap: round; stroke-dasharray: 70.4% 500%; }
.cap[b-es4kudw498] { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--mut2); }

/* D-227 — the second declared size, and the only one besides 48px. A popover is neither a page
   nor a card: WatchlistPicker's is 190px wide, and the stacked 48px ring fills it top to bottom
   and reads as "the whole page is loading" rather than "this popover is." That is why the picker
   hand-rolled its own ring; this is that ring, moved into the component it was copied from, so
   the popover's pixels are unchanged and only the ownership moved.

   Stroke deliberately stays 4px. It is the one piece of ring geometry that is absolute rather
   than a percentage of the box, so it does not scale with the size — at 18px it reads as 28% of
   the diameter against 10% at 48px. Rescaling it to 1.5px, which would make the two provably one
   ring at two sizes, was put to the owner against this and rejected in favour of the shipped
   look. Everything else scales for free: r, cx, cy and the dasharray are percentages of the
   svg's own box.

   `flex: 0 1 auto` restores the CSS initial value that the base rule overrides with `1 0 auto`
   — the page ring grows to fill <main>, a ring inside a popover must not. */
.loading-ring.compact[b-es4kudw498] { flex-direction: row; gap: 8px; padding: 10px 4px; flex: 0 1 auto; }
.loading-ring.compact svg[b-es4kudw498] { width: 18px; height: 18px; }


@keyframes ring-spin-b-es4kudw498 { to { transform: rotate(270deg); } }

@media (prefers-reduced-motion: reduce) {
    .loading-ring svg[b-es4kudw498] { animation: none; }
}
/* /Components/LockGlyph.razor.rz.scp.css */
/* The mockup's `flex-shrink: 0`: every host is an inline-flex row, and a squeezed lock stops reading as one. */
.lock-glyph[b-vv1iqkp4uu] {
    flex-shrink: 0;
}
/* /Components/LogoLoader.razor.rz.scp.css */
/* 20px flat, not an em multiple: brand.md:91 states the floor in pixels — "Never
   below 16px; below 20px use the filled favicon tile" — and 20px is the smallest size
   at which the two-card outline mark is the sanctioned asset. An earlier 1.2em landed
   at ~18px and was quietly inside the tile's territory. Expressed in the same unit as
   the rule so a future font-size change cannot silently violate it. */
.logo-loader[b-akrfio9h4o] {
    width: 20px;
    height: 20px;
    flex: none;
    margin-right: 8px;
    vertical-align: -0.18em;
}

/* Colours exactly as the specimen (Cardstock Logo.dc.html:198–202): the back card is
   an outline, the front card is white-filled with the same dark stroke, and the line
   is Ledger Teal. An earlier pass swapped these for `currentColor` and the on-dark
   teal, reasoning about contrast against the indigo button — wrong, and visibly so.
   The filled front card makes the mark self-contained: it carries its own white
   ground, so it reads on indigo, on paper, on anything, and needs no per-surface
   variant. That is why the brand drew it this way. */
/* Filled, where the specimen leaves it open. The specimen sits on a white page, so
   an unfilled back card shows white and the mark reads as two cards; on the indigo
   button it showed INDIGO through the back card -- the "busy field" bleed the sheet's
   own Misuse row warns about ("The mark is drawn, not styled. If it needs an effect
   to work on a surface, the surface is wrong"). Owner ruled the button is an allowed
   surface here, so the mark carries its own ground instead: filling the back card is
   what the specimen's white page was already providing for free, not a recolour. */
.logo-loader .card-back rect[b-akrfio9h4o] {
    fill: #FFFFFF;
    stroke: #1C1C1E;
}

.logo-loader .card-front rect[b-akrfio9h4o] {
    fill: #FFFFFF;
    stroke: #1C1C1E;
}

.logo-loader .spark[b-akrfio9h4o] {
    stroke: #0E8A7B;
    stroke-dasharray: 16;
    stroke-dashoffset: 16;
    animation: csLoop-b-akrfio9h4o 1600ms linear 600ms infinite;
}

.logo-loader .dot[b-akrfio9h4o] {
    fill: #0E8A7B;
    transform-origin: 23.5px 12.5px;
    opacity: 0;
    animation: csDotLoop-b-akrfio9h4o 1600ms linear 600ms infinite;
}

.logo-loader .card-back rect[b-akrfio9h4o] {
    animation: csBack-b-akrfio9h4o 460ms cubic-bezier(0.22, 0.8, 0.3, 1) both;
}

.logo-loader .card-front[b-akrfio9h4o] {
    animation: csFront-b-akrfio9h4o 460ms cubic-bezier(0.22, 0.8, 0.3, 1) 130ms both;
}

/* Verbatim from Cardstock Logo.dc.html:20–25. */
@keyframes csBack-b-akrfio9h4o {
    from { opacity: 0; transform: translateX(-7px); }
    to { opacity: 1; transform: translateX(0); }
}

@keyframes csFront-b-akrfio9h4o {
    from { opacity: 0; transform: translateX(8px); }
    to { opacity: 1; transform: translateX(0); }
}

@keyframes csLoop-b-akrfio9h4o {
    0% { stroke-dashoffset: 16; }
    32% { stroke-dashoffset: 0; }
    55% { stroke-dashoffset: 0; }
    87% { stroke-dashoffset: -16; }
    100% { stroke-dashoffset: -16; }
}

@keyframes csDotLoop-b-akrfio9h4o {
    0%, 30% { opacity: 0; transform: scale(0.2); }
    38%, 80% { opacity: 1; transform: scale(1); }
    87%, 100% { opacity: 0; transform: scale(0.35); }
}

/* Same posture as the D-114 ring: motion stops, the mark stays. It resolves to the
   finished logo -- sparkline drawn, dot present -- so the button still shows the
   brand rather than a half-drawn line, and `disabled` + `aria-busy` carry the state. */
@media (prefers-reduced-motion: reduce) {
    .logo-loader .spark[b-akrfio9h4o],
    .logo-loader .dot[b-akrfio9h4o],
    .logo-loader .card-back rect[b-akrfio9h4o],
    .logo-loader .card-front[b-akrfio9h4o] {
        animation: none;
    }

    .logo-loader .spark[b-akrfio9h4o] { stroke-dashoffset: 0; }
    .logo-loader .dot[b-akrfio9h4o] { opacity: 1; }
}
/* /Components/Pager.razor.rz.scp.css */
/* The ledger's pager rules (D-090), renamed when the control was lifted out of SalesLedger
   (D-234 walk finding 4). Buttons wear the ledger's tier-chip recipe; the range label is
   mono like the ledger's count line. The rule above the bar is this control's own top edge,
   standalone, for the ledger -- the one host that does not weld the bar into another box. A
   host that does weld it in — the Screener's and now Browse's two rosters, app.css's
   .roster-paged — overrides border-top away, because drawing it there would double the rule
   the table's own last row already carries. */
.pg-bar[b-a3rb4950w4] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 10px 16px;
    border-top: 1px solid var(--line);
    position: relative;
}

/* Busy — the turn that has to fetch (D-276 UAT round 3). The control the feed's pager replaced
   greyed itself out and took `cursor: progress` while its fetch was away (.af-more:disabled,
   deleted in cf6d477); this is that intent on the shared bar, with the third entry of D-139's
   loading vocabulary — the compact ring, for a control's own panel — instead of nothing.
   `.pg-btn:disabled` already sets `not-allowed`, and a disabled-because-busy button is waiting
   rather than refusing, so it is overridden here rather than left to say the wrong thing. */
.pg-bar.busy[b-a3rb4950w4], .pg-bar.busy .pg-btn:disabled[b-a3rb4950w4] {
    cursor: progress;
}

/* Out of flow deliberately: in flow the ring is the tallest item in the bar — 10px of its own
   padding around an 18px svg (LoadingRing.razor.css) against a 24px button — so the bar would
   grow past the 10 + 24 + 10 + 1 this stylesheet declares. That sum had a second reader until
   D-284 slice 4: Home's feed counted the bar into its own card floor, and its test recomputed it
   from this file. The digest that replaced that feed draws no pager and measures its own more-line,
   so nothing checks the arithmetic now — what LoadingRingLayoutTests still reads back from here is
   the ring's position and the side padding it aligns to, not the 45. Out of flow the ring cannot
   move it, whatever it measures. Absolute against the bar's padding box, so `right` lands on the
   same edge the buttons stop at. `top` and `bottom` are both non-auto, which resolves the box's
   height between them rather than placing it at a static position — so the bar's own `align-items`
   plays no part here, and none is needed: the ring centres its svg and caption itself,
   `.loading-ring` being a centred flex box on both axes. The right end is where the affordance
   belongs: only a forward turn can outrun the rows in hand, so Next is the button that waits. */
.pg-bar[b-a3rb4950w4]  .loading-ring.compact {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 16px;
}

.pg-btn[b-a3rb4950w4] {
    height: 24px;
    border-radius: 5px;
    padding: 0 9px;
    font-size: 12px;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    cursor: pointer;
}

.pg-btn:hover:not(:disabled)[b-a3rb4950w4] {
    background: var(--hov);
}

.pg-btn:disabled[b-a3rb4950w4] {
    color: var(--mut2);
    cursor: not-allowed;
}

.pg-label[b-a3rb4950w4] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11.5px;
    color: var(--mut2);
}
/* /Components/Screener/ActivityLog.razor.rz.scp.css */
/* D-284 slice 3 §3.3–§4 — the activity log as a normal table (the owner's 2026-09-17 UAT ruling). Every rule that
   is not this component's own is taken from the table directly above it on the page: RosterTable.razor.css's
   .roster-table (the panel), .rt-head / .rt-head-cell (the header box and its cells), .rt-row (the row box and its
   separator), and app.css's `--table-head-*` for the header face — 12.5px Inter at 600,
   letter-spaced and uppercased, which is the Home band's header and what D-234 walk finding 3 put on this page.

   ONE THING THE RESULTS TABLE'S HEADER DOES THAT THIS ONE DOES NOT: stick. A sticky header needs the page to be its
   scrollport, and any overflow value on an ancestor makes that ancestor the scrollport instead
   (RosterTable.razor.css's .roster-table, the D-118 lesson). .al-scroll is kept as the fallback for a window narrower than the
   table, so the header scrolls with its rows rather than pinning under the nav. */
.al-scroll[b-oymdsh06gq] { overflow-x: auto; }

/* .roster-table: the --card panel, the --line edge, the 10px radius, and no padding of its own — the rows carry the
   gutters. min-width is the table's own minimum, so the scroller above engages only below it: the seven tracks at
   their defaults (26 + 268 + 184 + 78 + 212 + 122 + 60 = 950, ActivityLog.razor's Columns array plus GlyphTrack),
   the row's 16px gutters (13px of padding plus the 3px edge, 16px right), and the panel's own two borders, which
   border-box counts in — 984. At a 1280px viewport with the rail open the column is 990px (1280 − 40 <main>
   gutters − 232 rail − 18 gap), so it fits with 6px to spare. A drag cannot spend that: the grip's budget is
   measured from this container, so the columns can only trade width with each other (ColumnSeams).
   `The_header_labels_centre_clear_of_the_grips_and_the_table_fits_1280` recomputes that sum rather than
   trusting it. */
.al-table[b-oymdsh06gq] {
    min-width: 984px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    overflow: hidden;
}

/* .rt-head, .rt-row: one grid for every row kind of the table, so a header cell sits over its own column. The 3px
   left edge is transparent on all of them and coloured only on a personal row (below), which keeps the tracks
   aligned rather than shifting the row that carries it. 16px gutters both sides — 13px of padding plus the edge on
   the left.

   The tracks are NOT declared here: the columns are draggable (the owner's 2026-09-17 re-walk), so the template is
   the component's, written inline on the header and on every row from one GridTemplate() — ActivityLog.razor's
   Columns array owns the default widths and says how each was sized, and a copy of those numbers here would be
   right only until the first grip moved. The four fixed tracks are each measured content plus a little; the three
   flexible ones open at what their own content needs and take the spare. A name or a sentence longer than its
   track still ellipsizes, and carries its whole text as a title, which is this slice's rule for clipping text. */
.al-head[b-oymdsh06gq], .al-row[b-oymdsh06gq] {
    display: grid;
    align-items: center;
    padding: 7px 16px 7px 13px;
    border-left: 3px solid transparent;
}

/* No column gap, as the roster has none (RosterTable.razor.css's .rt-head, .rt-row). A ROW's cells carry the separation as their own
   right padding instead, because this table's VALUES were ruled left and right in this slice (§4.9): a right-aligned
   price ends exactly where the next column's content begins, which no other roster has to solve — they centre every
   body cell (RosterTable.razor.css's .rt-cell, HoldingsTable.razor.css's .h-row > div, TransactionsLedger.razor.css's .l-row > div) and so declare
   no gutter at all. 6px is the site's own spacing between two text columns, not a new number: the Home band opens
   exactly that much between its tracks (app.css:986, `column-gap: 6px` on .band-header-row, .band-row); the sales
   ledger's is 8 (SalesLedger.razor.css:123).
   Three of the four right-aligned tracks carry this 6px on top of the content width they were sized to
   (ActivityLog.razor's Columns), so nothing they hold renders any narrower than the string it was measured from.
   TIME, the last of them, carries none — see the :last-child rule below.

   THE HEADER CELLS CARRY NONE OF IT. What holds a header label off the grip's ink is that the label is centred
   (below) — the one mechanism the other four tables use, and the whole of it. A gutter under a centred label would
   be a second mechanism doing nothing: measured in headless Chrome at the real 990px column (2026-09-18), centring
   alone leaves 13.5–113.3px of ink between a label and its grip, where this gutter bought 6.2–6.6px. */
.al-head > *[b-oymdsh06gq], .al-row > *[b-oymdsh06gq] { min-width: 0; }
.al-row > *[b-oymdsh06gq] { padding-right: 6px; }

/* Except the last cell. The gutter's whole reason above is that a right-aligned value ends exactly where the NEXT
   column's content begins — TIME has no next column: to its right is the row's own 16px padding and then the panel
   edge. A gutter there held 6px open for nothing, which is what the owner had removed from the header on this same
   walk, and those 6px are what paid for PRICE's widening (ActivityLog.razor's Columns). RosterTable, the table this
   one takes its chrome from, declares no per-cell gutter at all (RosterTable.razor.css's .rt-head, .rt-row and .rt-cell). */
.al-row > *:last-child[b-oymdsh06gq] { padding-right: 0; }

/* .rt-head's box and the app's table face (app.css `--table-head-*`, D-298 UAT walk 2026-10-08, finding 2). --mut2 on --card, a pair ContrastTests pins
   (The_text_hues_clear_AA_on_every_surface). Not sticky — see the file header. */
.al-head[b-oymdsh06gq] {
    padding-top: 6px;
    padding-bottom: 6px;
    background: var(--card);
    border-bottom: 1px solid var(--line);
    font: var(--table-head-font);
    letter-spacing: var(--table-head-tracking);
    text-transform: uppercase;
    color: var(--mut2);
}

/* .rt-head-cell, minus everything that sorts: this table has no sort (the header would throw away the query's
   pinning of a personal sell signal ahead of its day).

   THE LABEL CENTRES, whatever the cell under it does. That is the site's one rule for a header label, and the
   results table directly above this one is the proof that it does not follow the body: ScreenResults re-rules the
   CARD column's body cell to the left (ScreenResults.razor.css's .rt-cell-card) and leaves its header centred by
   RosterTable.razor.css's .rt-head-cell — CARD sits centred over left-aligned card names on the shipped Screener page.
   HoldingsTable.razor.css's .h-head > div and TransactionsLedger.razor.css's .l-head > div declare the same rule.
   It is also the whole of how those tables keep a label off a resize grip: a centred label never reaches the cell's
   right edge, so the grip's ink lands in blank space. This table shipped its headers ruled left and right to match
   its cells, which is what put PRICE, YOURS, ACTION and TIME against their pipes (the owner, the ace294e walk:
   "the headers look exactly the same. They're butted up against the pipes"). */
.al-h[b-oymdsh06gq] { position: relative; text-align: center; user-select: none; }

/* A label dragged narrower than its own text ellipsizes. It is the label and not the cell that clips, so that
   .al-h keeps no overflow value of its own — the grip is its positioned child, and a clipping ancestor is how a
   handle at a box's edge gets cut off. */
.al-h-label[b-oymdsh06gq] { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* .rt-grip — the same six declarations as .rt-grip, .h-grip and .l-grip, plus the Screener page's own grip hover
   (ScreenResults.razor.css's .rt-grip:hover, the results table directly above this one). The box sits inside the cell against
   its right edge, exactly as those three do, and the centred label above never reaches it. It hung half outside the
   cell (transform: translateX(50%)) while the labels were ruled to the seam; centring the labels is what made that
   a difference with no reason, so it is gone and the grip is the shipped one, declaration for declaration. */
.al-grip[b-oymdsh06gq] {
    position: absolute;
    right: 0;
    top: 0;
    cursor: col-resize;
    color: var(--line3);
    touch-action: none;
}
.al-grip:hover[b-oymdsh06gq] { color: var(--acc); }

/* The day divider (:250) — a row of the table one cell wide, so the counts sit on the day and not on a column. */
.al-day[b-oymdsh06gq] { background: var(--mutbg); border-bottom: 1px solid var(--line); }
.al-day-cell[b-oymdsh06gq] { display: flex; align-items: baseline; gap: 10px; padding: 8px 16px 6px; }
.al-day-date[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; font-weight: 700; color: var(--ink); }
.al-day-cont[b-oymdsh06gq], .al-day-counts[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--mut2); }
.al-spacer[b-oymdsh06gq] { flex: 1; }

/* .rt-row's separator. The row keeps its two-line height rather than the results table's 78px, which is that
   table's 48×66 thumbnail and nothing this row carries. */
.al-row[b-oymdsh06gq] { border-bottom: 1px solid var(--line4); }
/* Ruling R-j (D-298 UAT walk 2026-10-08, finding 5, "all the corners"): the last row reaches the box's rounded foot and paints nothing
   there but its rule, which sits 1px above the box's own bottom border, only doubles it. So the last row drops it, as the
   three ledgers' last rows do. The pager is never welded here: ActivityMode renders it below this box, outside .al-table,
   so the row's rule never parts it from anything. */
.al-row:last-child[b-oymdsh06gq] { border-bottom: none; }
.al-row:hover[b-oymdsh06gq] { background: var(--hov); }
/* The personal sell signal's edge: --neg2 is a fill hue, legal as a border and never as text (ContrastTests'
   A_state_fill_hue_is_never_used_as_text). The edge is one of four cues — glyph, badge text, edge, action — so colour
   never carries the state alone. */
.al-row.is-personal[b-oymdsh06gq] { border-left-color: var(--neg2); }

.al-glyph-cell[b-oymdsh06gq] { text-align: center; }
.al-glyph[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 13px; }
.al-glyph.pos[b-oymdsh06gq] { color: var(--pos); }
.al-glyph.neg[b-oymdsh06gq] { color: var(--neg); }
.al-glyph.warn[b-oymdsh06gq] { color: var(--warnInk); }

.al-name-line[b-oymdsh06gq] { display: flex; align-items: center; gap: 6px; min-width: 0; }
.al-name[b-oymdsh06gq] { font-size: 14px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: none; }
a.al-name:hover[b-oymdsh06gq] { text-decoration: underline; }
.al-sub[b-oymdsh06gq] { font-size: 12.5px; color: var(--mut2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* HELD · SELL SIGNAL is --neg on the negative tint, a pair ContrastTests pins over --card
   (Text_on_the_translucent_warn_fill_clears_AA). The tint is translucent, so it is laid over a --card layer of the
   badge's own: over a hovered row's --hov the bare tint measured under AA, and with the layer the rendered pair is the
   pinned one on any row background (TintOverCardCssTests holds the layer). HELD wears the Binder's ACTIVE pill's
   dress (D-301 row 62, ruled 2026-10-18; HoldingsTable.razor.css's .h-chip.is-active): --pos on the positive tint,
   laid over its own --card layer the same way, with the pill's hairline. */
.al-badge[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; border-radius: 3px; padding: 1px 5px; white-space: nowrap; flex-shrink: 0; cursor: help; }
.al-badge.is-sell[b-oymdsh06gq] { color: var(--neg); background: linear-gradient(var(--negBg), var(--negBg)), var(--card); }
.al-badge.is-held[b-oymdsh06gq] { color: var(--pos); background: linear-gradient(var(--posBg), var(--posBg)), var(--card); border: 1px solid color-mix(in srgb, var(--pos) 30%, transparent); }

.al-what-line[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.al-detail[b-oymdsh06gq] { font-size: 12.5px; color: var(--mut2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.al-price[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700; text-align: right; color: var(--ink); }

/* 212px is the measured width of the widest ordinary "yours" line — `yours · 2 @ $550.00 avg · +$200.00`, 203px at
   12px Inter — plus the row's 6px cell gutter above, which is why the number is 6 more than the 206 that first held
   it: the line renders in the same 206px it always did. 206 is what the owner saw clipped to "+$73…." in a 176px column on
   the 2026-09-17 walk. It flexes above that so a larger lot's line (`yours · 12 @ $5,550.00 avg · +$21,200.00`,
   235px) fits on a wide window. */
.al-yours[b-oymdsh06gq] { text-align: right; }
.al-yours-pct[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; }
.al-yours-pct.pos[b-oymdsh06gq] { color: var(--pos); }
.al-yours-pct.neg[b-oymdsh06gq] { color: var(--neg); }
.al-yours-pct.flat[b-oymdsh06gq] { color: var(--mut); }
.al-yours-sub[b-oymdsh06gq] { font-size: 12px; color: var(--mut2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.al-net[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; color: var(--ink); }

.al-action[b-oymdsh06gq] { display: flex; justify-content: flex-end; }
/* Sell in Binder → is --neg on the negative tint, pinned over --card; the tint is laid over a --card layer of its own,
   as the sell badge's is, so a hovered row cannot lighten or darken the pair (TintOverCardCssTests). */
.al-sell[b-oymdsh06gq] { height: 24px; padding: 0 9px; border-radius: 5px; font-size: 12.5px; font-weight: 600; white-space: nowrap; display: inline-flex; align-items: center; color: var(--neg); background: linear-gradient(var(--negBg), var(--negBg)), var(--card); border: 1px solid transparent; text-decoration: none; }
.al-time[b-oymdsh06gq] { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--mut2); text-align: right; white-space: nowrap; }

.al-empty[b-oymdsh06gq] { padding: 14px 16px; font-size: 13.5px; color: var(--mut); border-bottom: 1px solid var(--line4); }
.al-created-what[b-oymdsh06gq] { font-size: 12.5px; color: var(--mut); line-height: 1.45; white-space: normal; }
.al-created-sub[b-oymdsh06gq] { font-size: 12px; color: var(--mut2); }

/* The Binder's EST badge, declared here as HoldingsTable.razor.css and HoldingsGallery.razor.css each declare it
   (plan ruling 11) — --warnInk on --warnBg, a pair pinned over --card in Text_on_the_translucent_warn_fill_clears_AA.
   Here it sits on a log row that hovers to --hov, so the tint is laid over a --card layer of its own and the rendered
   pair stays the pinned one (TintOverCardCssTests). */
.est[b-oymdsh06gq] { display: inline-block; margin-left: 6px; padding: 0 5px; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; color: var(--warnInk); background: linear-gradient(var(--warnBg), var(--warnBg)), var(--card); border-radius: 3px; vertical-align: middle; cursor: help; }
/* /Components/Screener/ActivityMode.razor.rz.scp.css */
/* D-284 slice 3 §3: one column, 12px gaps (the rework's :228). The page is the scroller, as it is for results. */
.activity[b-dkspv0qzbz] { display: flex; flex-direction: column; gap: 12px; padding-bottom: 14px; }

/* The story strip (:230): --ink on --accBg with the accent border at 25% — the filter chip's pair, which
   ContrastTests already pins; the ✕ is --mut on --accBg, pinned beside it. The border is FilterChip's color-mix over
   --acc rather than the rework's rgba(74, 99, 208, 0.25): the same light value, and a border that follows dark mode. */
.act-story[b-dkspv0qzbz] { display: flex; align-items: flex-start; gap: 12px; background: var(--accBg); border: 1px solid color-mix(in srgb, var(--acc) 25%, transparent); border-radius: 8px; padding: 11px 14px; font-size: 13.5px; color: var(--ink); line-height: 1.55; }
.act-story-text[b-dkspv0qzbz] { flex: 1; min-width: 0; }
.act-story-dismiss[b-dkspv0qzbz] { background: none; border: none; cursor: pointer; color: var(--mut); font-size: 15px; line-height: 20px; padding: 0 2px; flex-shrink: 0; }
.act-story-dismiss:hover[b-dkspv0qzbz] { color: var(--ink); }

/* The pending card (:236): dashed amber, ◆ in --warnInk on --card (pinned). */
.act-pending[b-dkspv0qzbz] { display: flex; align-items: flex-start; gap: 12px; background: var(--card); border: 1px dashed rgba(176, 127, 26, 0.55); border-radius: 8px; padding: 10px 14px; }
.act-pending-glyph[b-dkspv0qzbz] { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: var(--warnInk); line-height: 20px; }
.act-pending-main[b-dkspv0qzbz] { flex: 1; min-width: 0; }
.act-pending-title[b-dkspv0qzbz] { font-size: 13.5px; font-weight: 600; line-height: 20px; color: var(--ink); }
.act-pending-meta[b-dkspv0qzbz] { font-family: 'JetBrains Mono', monospace; font-weight: 500; color: var(--mut); }
.act-pending-sub[b-dkspv0qzbz] { font-size: 12.5px; color: var(--mut2); margin-top: 6px; line-height: 1.5; }
.act-undo[b-dkspv0qzbz] { background: var(--card); border: 1px solid var(--line); border-radius: 6px; height: 26px; padding: 0 10px; font-size: 13px; color: var(--ink); cursor: pointer; flex-shrink: 0; }
.act-undo:hover[b-dkspv0qzbz] { background: var(--hov); }

.act-footnote[b-dkspv0qzbz] { margin: 0; padding: 0 2px; font-size: 12px; color: var(--mut2); }

/* The app-wide error look (app.css, D-213), restated because a scoped rule outranks the global one. */
.act-error p[b-dkspv0qzbz], .act-error-line[b-dkspv0qzbz] { margin: 0 0 6px; font-size: 13.5px; color: var(--neg); border-left: 3px solid var(--neg); padding-left: 8px; }
/* /Components/Screener/BacktestChart.razor.rz.scp.css */
/* Follows the Binder chart's PerformanceChart.razor.css: same gutter, readout and axis type. The index
   line takes the Binder chart's index stroke (--mut2); the screen line takes the Binder line's --acc. */
.bt-plotrow[b-cwltm0x7fy] { display: flex; gap: 8px; }
.bt-gut[b-cwltm0x7fy] { position: relative; width: 44px; flex-shrink: 0; }
.bt-gut span[b-cwltm0x7fy] { position: absolute; right: 0; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 10.5px; color: var(--mut2); }
.bt-ymax[b-cwltm0x7fy] { top: 2px; }
.bt-ymin[b-cwltm0x7fy] { bottom: 2px; }
.bt-y100[b-cwltm0x7fy] { transform: translateY(-50%); }
.bt-plot[b-cwltm0x7fy] { position: relative; flex: 1; min-width: 0; height: 200px; cursor: crosshair; }
.bt-plot svg[b-cwltm0x7fy] { display: block; width: 100%; height: 200px; }
/* A calendar date with nothing stored: the lines break, and the column says where. */
.bt-gap[b-cwltm0x7fy] { fill: var(--mutbg); }
.bt-base[b-cwltm0x7fy] { stroke: var(--line3); stroke-dasharray: 4 4; }
.bt-index[b-cwltm0x7fy] { stroke: var(--mut2); stroke-width: 1.5; }
.bt-screen[b-cwltm0x7fy] { stroke: var(--acc); stroke-width: 2; }
.bt-hits[b-cwltm0x7fy] { position: absolute; inset: 0; display: flex; }
.bt-hit[b-cwltm0x7fy] { flex: 1; }
.bt-vline[b-cwltm0x7fy] { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line3); pointer-events: none; transform: translateX(-50%); }
.bt-tip[b-cwltm0x7fy] { position: absolute; top: 8px; left: 8px; background: var(--tooltipBg); border: 1px solid var(--line); border-radius: 6px; padding: 5px 9px; pointer-events: none; z-index: 4; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; }
.bt-tip-m[b-cwltm0x7fy] { font-size: 11px; color: var(--mut2); }
.bt-tip-s[b-cwltm0x7fy] { font-size: 12.5px; font-weight: 700; color: var(--acc); }
.bt-tip-i[b-cwltm0x7fy] { font-size: 12.5px; color: var(--mut); }
.bt-tip-g[b-cwltm0x7fy] { font-size: 12.5px; color: var(--mut); }
.bt-xax[b-cwltm0x7fy] { position: relative; height: 16px; margin-top: 4px; margin-left: 52px; }
.bt-x[b-cwltm0x7fy] { position: absolute; top: 0; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 10.5px; color: var(--mut2); white-space: nowrap; }
.bt-x-start[b-cwltm0x7fy] { transform: none; }
.bt-x-middle[b-cwltm0x7fy] { transform: translateX(-50%); }
.bt-x-end[b-cwltm0x7fy] { transform: translateX(-100%); }
/* /Components/Screener/BacktestChecks.razor.rz.scp.css */
/* D-291 spec §5.1: the backtest's Checks panel. Tokens only, so the theme and colour-blind mode swap them. */

/* The frame is the entries table's card (BacktestEntries.razor.css .bt-entries). No overflow: hidden — the focus ring
   is an outline drawn outside the box (app.css :focus-visible) and would be clipped. */
.chk-panel[b-ckiiu9butv] { background: var(--card); border: 1px solid var(--line); border-radius: 8px; }

/* The header is one button across the full width; app.css already strips a button's border and fill and hands it the
   page's font. It hovers to --hov and is never tinted. Its corners are the card's, inside the 1px border: the top
   pair while the rows show, all four when the header is the whole panel. */
.chk-head[b-ckiiu9butv] { display: flex; align-items: center; gap: 10px; width: 100%; padding: 11px 14px 9px 14px; border-radius: 7px 7px 0 0; text-align: left; }
.chk-head[aria-expanded="false"][b-ckiiu9butv] { border-radius: 7px; }
.chk-head:hover[b-ckiiu9butv] { background: var(--hov); }
.chk-head-text[b-ckiiu9butv] { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
.chk-title[b-ckiiu9butv] { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.chk-tally[b-ckiiu9butv] { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--mut2); }

/* Collapsed only: the flagged checks, each in the warning ink whatever its kind and saying its kind in words (owner,
   2026-10-02, D-296 UAT). With many flagged they wrap to further lines, each name whole; none is dropped. */
.chk-flagged[b-ckiiu9butv] { min-width: 0; font-size: 12.5px; font-weight: 600; line-height: 1.5; }
.chk-flag[b-ckiiu9butv] { white-space: nowrap; }
.chk-flag.is-risk[b-ckiiu9butv], .chk-flag.is-data[b-ckiiu9butv] { color: var(--warnInk); }
/* brand.md §4: every glyph span names the mono face itself (GateGlyphCssTests). */
.chk-flag-mark[b-ckiiu9butv] { font-family: 'JetBrains Mono', monospace; margin-right: 5px; }
.chk-sep[b-ckiiu9butv] { font-weight: 400; color: var(--mut2); }

/* The chevron holds the far right whatever the text beside it does. --mut is a graphic here, not text. */
.chk-chevron[b-ckiiu9butv] { display: inline-flex; flex-shrink: 0; color: var(--mut); }

/* The rows' wrapper carries `hidden` while the panel is collapsed, so it declares NO display: an author display
   beats the browser's hidden, and the panel would never collapse while every component test stayed green
   (BacktestChecksCssTests). Its top border is the line under the header, so the line goes with the rows. */
.chk-rows[b-ckiiu9butv] { border-top: 1px solid var(--line); }

/* Four columns: mark · name · state · sentence. The name track holds the longest of the thirteen names on one line
   (D-291; BacktestChecksCssTests holds the track and the length it was sized for). */
.chk-row[b-ckiiu9butv] { display: grid; grid-template-columns: 18px 176px 150px 1fr; column-gap: 12px; align-items: baseline; padding: 9px 14px; border-bottom: 1px solid var(--line4); font-size: 12.5px; line-height: 1.5; color: var(--mut); }
.chk-row:last-child[b-ckiiu9butv] { border-bottom: none; }
/* brand.md §4 again: the mark column, at the size the marks were approved at. */
.chk-mark[b-ckiiu9butv] { font-family: 'JetBrains Mono', monospace; font-size: 17px; line-height: 1; text-align: center; }
.chk-name[b-ckiiu9butv] { font-size: 13px; font-weight: 600; color: var(--ink); }
.chk-name[title][b-ckiiu9butv], .chk-text[title][b-ckiiu9butv] { cursor: help; }
.chk-state[b-ckiiu9butv] { font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }

/* A flagged check is a warning, whatever its kind, and says its kind in words — FLAGGED · RISK, FLAGGED · DATA (owner,
   2026-10-02, D-296 UAT). So a flagged row takes the warning pair, --warnInk on the --warnBg tint, the tint laid over
   an opaque --card layer of the row's own (TintOverCardCssTests), so the contrast ContrastTests pins is the contrast
   drawn. Red is kept for what stops a test running: the failed state below. Rows do not hover. */
.chk-row.is-risk[b-ckiiu9butv], .chk-row.is-data[b-ckiiu9butv] { background: linear-gradient(var(--warnBg), var(--warnBg)), var(--card); color: var(--warnInk); }
.chk-row.is-risk .chk-name[b-ckiiu9butv], .chk-row.is-data .chk-name[b-ckiiu9butv] { color: var(--warnInk); }

/* Not built yet (D-087): the deferred tone. */
.chk-row.is-locked[b-ckiiu9butv] { color: var(--mut2); }
.chk-row.is-locked .chk-name[b-ckiiu9butv] { color: var(--mut); }

/* D-296 spec §4.5: check 14's row, from the approved render (option A, the companion, 2026-10-01). Its sentence cell
   carries the row's one control beside the sentence — Run fragility test, or Retry after a failure a retry can pass. */
.chk-text-run[b-ckiiu9butv] { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
/* The row's button is the app's plain secondary button (BacktestMode's .bt-retry): --ink on --card, --hov on hover. */
.chk-run[b-ckiiu9butv] { flex-shrink: 0; align-self: center; background: var(--card); color: var(--ink); border: 1px solid var(--line); border-radius: 6px; height: 28px; padding: 0 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.chk-run:hover[b-ckiiu9butv] { background: var(--hov); }

/* Running: the compact ring, the step in ink and the seconds muted — the backtest's own step line (.bt-step) at the
   row's size. The compact ring's padding is a popover's; inside a row it would push the line down. */
.chk-running[b-ckiiu9butv] { display: flex; align-items: center; gap: 8px; font-family: 'JetBrains Mono', monospace; font-size: 12.5px; color: var(--ink); }
.chk-running[b-ckiiu9butv]  .loading-ring.compact { padding: 0; }
/* The ring spins without its "Loading…" (owner, 2026-10-05 UAT): the step beside it already says what is running, and
   the caption's 55px wrapped the step line at 1280px. Here only — every other ring keeps its caption. */
.chk-running[b-ckiiu9butv]  .loading-ring .cap { display: none; }
.chk-running-time[b-ckiiu9butv] { color: var(--mut); }

/* Failed: the app's error look on the sentence (D-213, ErrorLookTests) — --neg and the 3px left rule, no glyph, so the
   mark column stays empty — and Retry beside it. */
.chk-fail[b-ckiiu9butv] { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.chk-fail-text[b-ckiiu9butv] { color: var(--neg); border-left: 3px solid var(--neg); padding-left: 8px; }

/* The table of every nudge hangs under its row, indented to the name column: the row's 14px padding, the 18px mark
   track and its 12px gap. It sits on --card, outside a flagged row's tint. */
.chk-frag[b-ckiiu9butv] { padding: 0 14px 12px 44px; border-bottom: 1px solid var(--line4); }
/* The header row and the value cells wear the app's table face (app.css's --table-*, D-298 UAT walk 2026-10-08, finding 2); line-height:
   inherit keeps the line box the `font` shorthand would reset. The table sizes to its content, so no label can outgrow
   a track. The from-value and the flag cell keep their own sans face below; a saved row keeps its weight. */
.chk-frag table[b-ckiiu9butv] { border-collapse: collapse; }
.chk-frag th[b-ckiiu9butv] { text-align: right; font: var(--table-head-font); line-height: inherit; letter-spacing: var(--table-head-tracking); text-transform: uppercase; color: var(--mut2); padding: 4px 0 5px 26px; white-space: nowrap; }
.chk-frag td[b-ckiiu9butv] { font: var(--table-cell-font); line-height: inherit; text-align: right; padding: 5px 0 5px 26px; color: var(--ink); border-top: 1px solid var(--line4); white-space: nowrap; }
.chk-frag th:first-child[b-ckiiu9butv], .chk-frag td:first-child[b-ckiiu9butv] { text-align: left; padding-left: 0; }
.chk-frag tr.is-saved td[b-ckiiu9butv] { font-weight: 700; }
.chk-frag-from[b-ckiiu9butv] { margin-left: 8px; font-family: 'Inter', system-ui, sans-serif; font-size: 12px; color: var(--mut2); }
/* The buy signals' change is context: muted, never a direction's hue. */
.chk-frag-change[b-ckiiu9butv] { margin-left: 6px; color: var(--mut2); }
.chk-frag td.is-pos[b-ckiiu9butv] { color: var(--pos); }
.chk-frag td.is-neg[b-ckiiu9butv] { color: var(--neg); }
.chk-frag td.is-none[b-ckiiu9butv] { color: var(--mut2); }
/* The mark column: too few and a skip's reason in words, muted; ⚠ for a nudge at half or less. */
.chk-frag td.chk-frag-flag[b-ckiiu9butv] { text-align: left; padding-left: 14px; font-family: 'Inter', system-ui, sans-serif; font-size: 12px; color: var(--mut2); }
/* brand.md §4: every glyph span names the mono face itself (GateGlyphCssTests). */
.chk-frag-mark[b-ckiiu9butv] { font-family: 'JetBrains Mono', monospace; font-size: 13px; }
/* A nudge at half or less is a warning (owner, 2026-10-02, D-296 UAT): its label and its mark in --warnInk; its from
   stays muted; its mean keeps its direction's hue. */
.chk-frag tr.is-half .chk-frag-label[b-ckiiu9butv], .chk-frag tr.is-half td.chk-frag-flag[b-ckiiu9butv] { color: var(--warnInk); }
/* A skipped nudge is muted throughout. */
.chk-frag tr.is-skipped td[b-ckiiu9butv], .chk-frag tr.is-skipped .chk-frag-from[b-ckiiu9butv] { color: var(--mut2); }
/* /Components/Screener/BacktestEntries.razor.rz.scp.css */
/* D-290 spec §6.5: the entries table (the rework's :550-562). */
.bt-entries[b-lj9q13cnco] { background: var(--card); border: 1px solid var(--line); border-radius: 8px; }
.bt-entries-head[b-lj9q13cnco] { display: flex; align-items: center; padding: 11px 14px 9px 14px; border-bottom: 1px solid var(--line); }
.bt-entries-title[b-lj9q13cnco] { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.bt-entries-meta[b-lj9q13cnco] { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--mut2); margin-left: 10px; }
.bt-spacer[b-lj9q13cnco] { flex: 1; }
.bt-csv[b-lj9q13cnco] { font-size: 12.5px; color: var(--acc); text-decoration: none; }
.bt-csv:hover[b-lj9q13cnco] { text-decoration: underline; }

/* The six tracks are not here: a grip moves them, so BacktestEntries.razor writes them on the header and on every row
   (GridTemplate, the owner's 2026-10-02 UAT ruling), at rest the rework's tracks (btGridCols) with the card track
   widened for CardCell's 48px thumbnail. */
.bt-grid[b-lj9q13cnco] { display: grid; gap: 8px; padding: 6px 14px; align-items: center; }
/* The app's table face (D-298 UAT walk 2026-10-08, finding 2); line-height: inherit keeps the line box the `font` shorthand would reset. */
.bt-cols[b-lj9q13cnco] { padding: 7px 14px; border-bottom: 1px solid var(--line); font: var(--table-head-font); line-height: inherit; letter-spacing: var(--table-head-tracking); color: var(--mut2); text-transform: uppercase; }
.bt-col[b-lj9q13cnco] { position: relative; text-align: center; border-radius: 4px; padding: 2px 0; user-select: none; }

/* A label dragged narrower than its text ellipsizes. The label clips, not the cell, so .bt-col keeps no overflow
   value of its own — the grip is its positioned child (ActivityLog.razor.css's .al-h-label, for the same reason). */
.bt-col-label[b-lj9q13cnco] { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* HoldingsTable's .h-sort: the sort control is a button inside the header cell, reset so it inherits the header's type
   — here its uppercase and tracking too, which a button does not inherit by default — with the --ink hover. Held to
   the cell's width so the label inside it still ellipsizes when a grip narrows the column. */
.bt-sort[b-lj9q13cnco] { background: none; border: 0; padding: 0; color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; max-width: 100%; }
.bt-sort:hover[b-lj9q13cnco] { color: var(--ink); }

/* The site's grip, declaration for declaration (.rt-grip, .h-grip, .l-grip, .al-grip), and its accent hover. */
.bt-grip[b-lj9q13cnco] {
    position: absolute;
    right: 0;
    top: 0;
    cursor: col-resize;
    color: var(--line3);
    touch-action: none;
}
.bt-grip:hover[b-lj9q13cnco] { color: var(--acc); }
.bt-col.is-selected[b-lj9q13cnco] { background: var(--accBg); }
.bt-row[b-lj9q13cnco] { border-bottom: 1px solid var(--line4); min-height: 36px; }
/* Ruling R-j (D-298 UAT walk 2026-10-08, finding 5, "all the corners"): the last row reaches the box's rounded foot and paints nothing
   there but its rule, which sits 1px above the box's own bottom border, only doubles it, and ran its square ends across
   the curve. So the last row drops it -- the smaller honest change than rounding a second line inside the first. Under
   the pager the row is not the last child and keeps the rule that parts it from the pager. */
.bt-row:last-child[b-lj9q13cnco] { border-bottom: none; }
.bt-row[b-lj9q13cnco]  .row-link { color: inherit; text-decoration: none; min-width: 0; }

/* A grip can take any fixed track to ColumnSeams' 52px floor, so a value wider than its column clips with an
   ellipsis rather than painting into the next one — ActivityLog.razor.css's rules: no cell keeps a content-sized
   minimum, and each value line is nowrap · hidden · ellipsis (the newest-sale line wraps instead, below). */
.bt-cols > *[b-lj9q13cnco], .bt-row > *[b-lj9q13cnco] { min-width: 0; }

/* The tier chip and re-entry marker lead CardCell's sub-line (its SubPrefix). */
.bt-tier[b-lj9q13cnco] { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; color: var(--ink); background: var(--mutbg); border: 1px solid var(--line); border-radius: 4px; padding: 0 5px; line-height: 16px; }
.bt-reentry[b-lj9q13cnco] { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--acc); cursor: help; }

.bt-date[b-lj9q13cnco] { font: var(--table-cell-font); line-height: inherit; color: var(--mut); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bt-price[b-lj9q13cnco] { font: var(--table-cell-font); line-height: inherit; text-align: center; align-self: stretch; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--ink); }
.bt-price-v[b-lj9q13cnco] { font-weight: 700; }
.bt-sale[b-lj9q13cnco] { font-size: 11px; font-weight: 400; color: var(--mut2); max-width: 100%; overflow: hidden; overflow-wrap: anywhere; }
.bt-cell[b-lj9q13cnco] { font: var(--table-cell-font); line-height: inherit; text-align: center; align-self: stretch; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.bt-cell.is-selected[b-lj9q13cnco] { background: var(--accBg); font-weight: 700; }
.bt-cell.is-strong[b-lj9q13cnco] { font-weight: 700; }
.bt-unrealized[b-lj9q13cnco] { font-size: 11px; color: var(--mut2); }
/* Each value line — a return cell's spans and the price itself — is a span of its own (a flex cell's bare text is an
   anonymous box no ellipsis reaches); held to the cell's width, it clips where the cell does. Centred as before — the
   cell's align-items. The newest-sale line under a carried price is not one of them: `newest sale MM-DD-YYYY` is 22
   characters at JetBrains Mono's 0.6em advance, 22 × 6.6px = 145px at 11px, wider than the 124px column at rest — so it
   wraps to two lines as it always did, held to the cell and broken anywhere (.bt-sale above) so it cannot reach the
   next column at the 52px floor either. */
.bt-cell > span[b-lj9q13cnco], .bt-price-v[b-lj9q13cnco] { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.is-pos[b-lj9q13cnco] { color: var(--pos); }
.is-neg[b-lj9q13cnco] { color: var(--neg); }
.is-mut[b-lj9q13cnco] { color: var(--mut); }
.is-mut2[b-lj9q13cnco] { color: var(--mut2); }
.is-acc[b-lj9q13cnco] { color: var(--acc); }
/* /Components/Screener/BacktestMode.razor.rz.scp.css */
/* D-290 spec §7: backtest mode, built from the rework (Cardstock Screener.rework-2026-09-28.html:333-565). One column,
   12px gaps, the page is the scroller — ActivityMode's frame. */
.backtest[b-t4jale0s0s] { display: flex; flex-direction: column; gap: 12px; padding-bottom: 14px; }

/* The controls panel (:335). */
.bt-controls[b-t4jale0s0s] { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px; display: flex; align-items: flex-start; gap: 26px; flex-wrap: wrap; }
.bt-field-horizon[b-t4jale0s0s] { min-width: 220px; }
.bt-label[b-t4jale0s0s] { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; color: var(--mut2); text-transform: uppercase; margin-bottom: 5px; }
.bt-entry-val[b-t4jale0s0s], .bt-range-val[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: var(--ink); line-height: 24px; }
.bt-nights[b-t4jale0s0s], .bt-unlock[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--mut2); }
.bt-unlock[b-t4jale0s0s] { margin-top: 5px; }
.bt-pills[b-t4jale0s0s] { display: flex; gap: 4px; }
.bt-pill-wrap[b-t4jale0s0s] { display: inline-flex; }
/* The rework's pill (:343): --mut on --card, the chosen one --card on --acc. */
.bt-pill[b-t4jale0s0s] { background: var(--card); color: var(--mut); border: 1px solid var(--line); border-radius: 5px; height: 24px; padding: 0 10px; font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 600; cursor: pointer; }
.bt-pill.is-on[b-t4jale0s0s] { background: var(--acc); color: var(--card); border-color: var(--acc); }
.bt-pill:disabled[b-t4jale0s0s] { background: var(--hov); color: var(--mut2); border-color: var(--line); cursor: not-allowed; }
.bt-h-na[b-t4jale0s0s] { font-size: 12.5px; color: var(--mut); line-height: 24px; }
.bt-h-off[b-t4jale0s0s] { font-size: 12.5px; color: var(--mut); line-height: 1.45; max-width: 250px; }
.bt-spacer[b-t4jale0s0s] { flex: 1; }
/* Run takes the app's primary button (ProfileCard's .btn-save): white on --btn. Disabled, the rework's grey (:378). */
.bt-run[b-t4jale0s0s] { align-self: center; background: var(--btn); color: #FFFFFF; border: none; border-radius: 6px; height: 32px; padding: 0 16px; font-size: 13.5px; font-weight: 600; cursor: pointer; }
.bt-run:hover[b-t4jale0s0s] { background: var(--btnH); }
.bt-run-wrap[b-t4jale0s0s] { display: inline-flex; align-self: center; }
.bt-run:disabled[b-t4jale0s0s] { background: var(--mutbg); color: var(--mut2); border: 1px solid var(--line); cursor: not-allowed; }

/* The floor note (:381): amber, --warnInk on --warnBg — the anchor-tier banner's pair, which ContrastTests pins. The
   running panel's stay line (D-291 §5.2) takes the same declaration rather than a copy of it. Its ground is the
   panel's --card, and ContrastTests pins the pair over that too (Text_on_the_translucent_warn_fill_clears_AA, the
   --warnInk on --warnBg over --card rows). */
.bt-floor[b-t4jale0s0s], .bt-stay[b-t4jale0s0s] { background: var(--warnBg); border: 1px solid color-mix(in srgb, var(--warn) 22%, transparent); border-radius: 8px; padding: 9px 12px; font-size: 12.5px; color: var(--warnInk); line-height: 1.5; }

/* Phase panels. */
.bt-idle[b-t4jale0s0s], .bt-running[b-t4jale0s0s], .bt-locked[b-t4jale0s0s], .bt-nofilters[b-t4jale0s0s], .bt-empty[b-t4jale0s0s], .bt-failed[b-t4jale0s0s] { background: var(--card); border: 1px solid var(--line); border-radius: 8px; }
.bt-idle[b-t4jale0s0s] { padding: 34px 24px; text-align: center; font-size: 14.5px; color: var(--mut); line-height: 1.6; }
.bt-running[b-t4jale0s0s] { padding: 22px 24px; }
.bt-running-row[b-t4jale0s0s] { display: flex; align-items: center; gap: 12px; }
.bt-scan[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; font-size: 13.5px; color: var(--acc); }
.bt-running-note[b-t4jale0s0s] { font-size: 12.5px; color: var(--mut2); margin-top: 9px; }
/* D-292 §5.2: the live step line, as approved in the companion (option A): the step in ink, the seconds muted, set in
   under the ring's row. */
.bt-step[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: var(--ink); margin: 2px 0 0 32px; }
.bt-step-time[b-t4jale0s0s] { color: var(--mut); }
/* The stay line (D-291 §5.2) leads the panel, above the ring: the mark, then the lead in bold and its sentence. Colour,
   border and padding are the floor note's, above; these add the row and the approved render's type size and spacing.
   The mark is a glyph span, so its rule names the mono face itself (GateGlyphCssTests). */
.bt-stay[b-t4jale0s0s] { display: flex; align-items: baseline; gap: 8px; font-size: 13px; margin-bottom: 14px; }
/* D-296 §4.7: the fragility test's stay line stands in the column, not in a panel, so the column's 12px gap spaces it
   and the running panel's bottom margin does not apply. */
.bt-stay.is-top[b-t4jale0s0s] { margin-bottom: 0; }
.bt-stay-lead[b-t4jale0s0s] { font-weight: 700; }
.bt-stay-mark[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; }
/* The failed state (:399): the app's error look — --neg with its left bar — and a plain Retry. */
.bt-failed[b-t4jale0s0s] { border-left: 3px solid var(--neg); padding: 16px 18px; display: flex; align-items: center; gap: 14px; }
.bt-fail-text[b-t4jale0s0s] { flex: 1; font-size: 13.5px; color: var(--neg); }
.bt-retry[b-t4jale0s0s], .bt-go-results[b-t4jale0s0s] { background: var(--card); border: 1px solid var(--line); border-radius: 6px; height: 28px; padding: 0 12px; font-size: 13px; color: var(--ink); cursor: pointer; }
.bt-retry:hover[b-t4jale0s0s], .bt-go-results:hover[b-t4jale0s0s] { background: var(--hov); }

/* Not available (:406). */
.bt-locked[b-t4jale0s0s] { padding: 18px 20px; }
.bt-locked-head[b-t4jale0s0s] { display: flex; align-items: center; gap: 8px; font-size: 14.5px; font-weight: 600; color: var(--ink); }
.bt-lock-glyph[b-t4jale0s0s] { color: var(--mut2); display: inline-flex; }
.bt-opens[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; font-size: 18px; font-weight: 700; color: var(--ink); margin-top: 10px; }
.bt-lead[b-t4jale0s0s] { font-size: 13.5px; color: var(--mut); margin-top: 6px; line-height: 1.5; max-width: 720px; }
.bt-lock-rows[b-t4jale0s0s] { margin-top: 12px; border-top: 1px solid var(--line4); }
.bt-lock-row[b-t4jale0s0s] { display: flex; gap: 16px; align-items: baseline; padding: 10px 0; border-bottom: 1px solid var(--line4); }
.bt-lock-f[b-t4jale0s0s] { flex: 1; font-family: 'JetBrains Mono', monospace; font-size: 12.5px; color: var(--ink); }
.bt-lock-when[b-t4jale0s0s] { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; color: var(--mut); text-align: right; }
.bt-lock-row.is-blocking .bt-lock-when[b-t4jale0s0s] { color: var(--ink); }

/* No filters (:424) and no entries (:430). */
.bt-nofilters[b-t4jale0s0s] { padding: 30px 24px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.bt-nofilters-text[b-t4jale0s0s] { font-size: 14.5px; color: var(--mut); }
.bt-empty[b-t4jale0s0s] { padding: 30px 24px; text-align: center; }
.bt-empty-title[b-t4jale0s0s] { font-size: 14.5px; font-weight: 600; color: var(--ink); }
.bt-empty-text[b-t4jale0s0s] { font-size: 13.5px; color: var(--mut); max-width: 620px; margin: 8px auto 0 auto; line-height: 1.55; }
/* /Components/Screener/BacktestResults.razor.rz.scp.css */
/* D-290 spec §6: a finished run (the rework's :436-565). */

/* The story (:437): ActivityMode's story strip — --ink on --accBg with the accent border at 25%. */
.bt-story[b-ytna5kpzzj] { background: var(--accBg); border: 1px solid color-mix(in srgb, var(--acc) 25%, transparent); border-radius: 8px; padding: 11px 14px; font-size: 13.5px; color: var(--ink); line-height: 1.55; }

.bt-chart-card[b-ytna5kpzzj], .bt-tile[b-ytna5kpzzj], .bt-hist[b-ytna5kpzzj] { background: var(--card); border: 1px solid var(--line); border-radius: 8px; }
.bt-chart-card[b-ytna5kpzzj] { padding: 13px 14px; }
.bt-chart-head[b-ytna5kpzzj] { display: flex; align-items: baseline; gap: 16px; margin-bottom: 10px; flex-wrap: wrap; }
.bt-chart-title[b-ytna5kpzzj] { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.bt-legend[b-ytna5kpzzj] { display: inline-flex; align-items: center; gap: 6px; font-family: 'JetBrains Mono', monospace; font-size: 13px; }
.bt-legend-screen[b-ytna5kpzzj] { font-weight: 700; color: var(--acc); }
.bt-legend-index[b-ytna5kpzzj] { color: var(--mut); }
/* The swatches follow BacktestChart's strokes: --acc for the screen, --mut2 for the index. */
.bt-swatch-screen[b-ytna5kpzzj] { width: 14px; height: 2px; background: var(--acc); }
.bt-swatch-index[b-ytna5kpzzj] { width: 14px; height: 0; border-top: 1.5px solid var(--mut2); }
.bt-gap-legend[b-ytna5kpzzj] { font-family: inherit; font-size: 12px; color: var(--mut2); }
.bt-swatch-gap[b-ytna5kpzzj] { width: 8px; height: 12px; background: var(--mutbg); border: 1px solid var(--line); }
.bt-spacer[b-ytna5kpzzj] { flex: 1; }
.bt-chart-range[b-ytna5kpzzj] { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--mut2); }

/* Tiles beside the histogram (:505). */
.bt-mid[b-ytna5kpzzj] { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); gap: 12px; align-items: stretch; }
.bt-tiles[b-ytna5kpzzj] { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; align-content: start; }
.bt-tile[b-ytna5kpzzj] { padding: 11px 14px; cursor: help; min-width: 0; }
.bt-tile-k[b-ytna5kpzzj] { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; color: var(--mut2); text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bt-tile-v[b-ytna5kpzzj] { font-family: 'JetBrains Mono', monospace; font-size: 21px; font-weight: 700; margin-top: 4px; white-space: nowrap; }
.bt-tile-sub[b-ytna5kpzzj] { font-size: 11.5px; color: var(--mut2); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-height: 1em; }
/* Direction text is --pos/--neg, never --pos2/--neg2; every value also carries its sign. */
.is-ink[b-ytna5kpzzj] { color: var(--ink); }
.is-pos[b-ytna5kpzzj] { color: var(--pos); }
.is-neg[b-ytna5kpzzj] { color: var(--neg); }
.is-mut[b-ytna5kpzzj] { color: var(--mut); }
.is-mut2[b-ytna5kpzzj] { color: var(--mut2); }
.is-acc[b-ytna5kpzzj] { color: var(--acc); }

.bt-hist[b-ytna5kpzzj] { padding: 13px 14px; display: flex; flex-direction: column; }
.bt-hist-head[b-ytna5kpzzj] { display: flex; align-items: baseline; gap: 8px; }
.bt-hist-title[b-ytna5kpzzj] { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.bt-hist-n[b-ytna5kpzzj] { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--mut2); }
.bt-no-bars[b-ytna5kpzzj] { font-size: 12.5px; color: var(--mut); margin-top: 12px; line-height: 1.5; }
.bt-bars[b-ytna5kpzzj] { display: flex; align-items: flex-end; gap: 7px; flex: 1; min-height: 110px; margin-top: 12px; }
.bt-bar[b-ytna5kpzzj] { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 4px; height: 100%; }
.bt-bar-n[b-ytna5kpzzj] { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--mut2); }
/* The palette's tinted fills (spec §6.3) at the rework's intensities (PAL.negBg(0.45), PAL.posBg(0.5)), mixed over the
   direction tokens so colourblind mode swaps the hue. The label under each bar carries the meaning. */
.bt-bar-fill[b-ytna5kpzzj] { width: 100%; border-radius: 3px 3px 0 0; }
.bt-bar-fill.is-loss[b-ytna5kpzzj] { background: color-mix(in srgb, var(--neg) 45%, transparent); }
.bt-bar-fill.is-gain[b-ytna5kpzzj] { background: color-mix(in srgb, var(--pos) 50%, transparent); }
.bt-bar-label[b-ytna5kpzzj] { font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--mut2); white-space: nowrap; }

/* The age note (:547): grey. */
.bt-age-note[b-ytna5kpzzj] { background: var(--mutbg); border: 1px solid var(--line); border-radius: 8px; padding: 9px 12px; font-size: 12.5px; color: var(--mut); line-height: 1.5; }

.bt-footnote[b-ytna5kpzzj] { margin: 0; padding: 0 2px 4px 2px; font-size: 12px; color: var(--mut2); line-height: 1.5; }
/* /Components/Screener/DeleteScreenConfirm.razor.rz.scp.css */
/* D-284 slice 2: the purchases line (rework Screener :76). ◆ paired with --warnInk — the same glyph and
   tone slice 1 gave the rules-changed row, so the warning is never colour alone (brand.md §4). The dialog's
   ground is --card (app.css .modal-dialog), for which ContrastTests already carries --warnInk's rows.
   The space is below the line, not above it: a top margin collapses into .modal-summary's margin-bottom
   (app.css:293) and does nothing, while .modal-body has no top margin, so the body would sit flush against
   the line. margin-bottom: 8px is the dialog's own rhythm (the h3 at app.css:279, the summary at :293).
   D-291 §5.2: the run line takes this same form, directly above the body, so two lines stand 8px apart. */
.modal-purchases[b-la2y5vhljv] { display: flex; gap: 6px; align-items: flex-start; margin-bottom: 8px; font-size: 12.5px; line-height: 1.45; color: var(--warnInk); }

/* Both lines' ◆, in the glyph face by name (brand.md §4); GateGlyphCssTests registers the selector. It replaces a
   rule that drew the purchases line's mark by position, in a stack that led with ui-monospace — SF Mono on WebKit,
   where every other ◆ in the app is JetBrains Mono. By position it would have drawn the run line's mark too, and
   outranked any class given to it. */
.modal-line-glyph[b-la2y5vhljv] { font-family: 'JetBrains Mono', monospace; }
/* /Components/Screener/FilterEditor.razor.rz.scp.css */
/* Level 2 of the filter popover — `Cardstock Screener.dc.html:113–170`. The box is
   ScreenFilterRow's; these are the controls inside it. */

.fe-head[b-4pkczubl49] {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 2px 2px 7px;
    border-bottom: 1px solid var(--line4);
}

.fe-back[b-4pkczubl49] {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--mut);
    font-size: 16px;
    padding: 2px 6px;
    border-radius: 4px;
    line-height: 1;
}

.fe-back:hover[b-4pkczubl49] { background: var(--hov); color: var(--ink); }

.fe-name[b-4pkczubl49] {
    font-size: 13.5px;
    font-weight: 600;
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fe-badge[b-4pkczubl49] {
    font-size: 10.5px;
    font-weight: 600;
    color: var(--warnInk);
    background: var(--warnBg);
    padding: 1px 5px;
    border-radius: 3px;
    white-space: nowrap;
}

.fe-block[b-4pkczubl49] { padding: 9px 4px 0; }

.fe-label[b-4pkczubl49] {
    font: 600 11px 'Inter Tight', sans-serif;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mut2);
    margin-bottom: 5px;
}

.fe-pills[b-4pkczubl49] { display: flex; gap: 4px; flex-wrap: wrap; }

.fe-window[b-4pkczubl49],
.fe-op[b-4pkczubl49] {
    background: var(--card);
    color: var(--mut);
    border: 1px solid var(--line);
    border-radius: 5px;
    height: 24px;
    padding: 0 9px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}

/* --btn, not --acc: white on --btn is the pairing ContrastTests.TextOnFill pins, and dark's
   --acc is a light hue that white would fail against. */
.fe-window.on[b-4pkczubl49],
.fe-op.on[b-4pkczubl49] { background: var(--btn); color: #FFFFFF; border-color: var(--btn); }

.fe-values[b-4pkczubl49] { display: flex; gap: 6px; align-items: center; margin-top: 8px; }

.fe-unit[b-4pkczubl49] { font-size: 13px; color: var(--mut); }

.fe-dash[b-4pkczubl49] { color: var(--mut2); }

.fe-value[b-4pkczubl49] {
    width: 84px;
    height: 26px;
    border: 1px solid var(--line);
    border-radius: 5px;
    background: var(--bg);
    padding: 0 8px;
    font-size: 13px;
    color: var(--ink);
    box-sizing: border-box;
}

.fe-search[b-4pkczubl49] { margin-top: 9px; }

.fe-picked[b-4pkczubl49] { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }

.fe-options[b-4pkczubl49] { max-height: 168px; overflow-y: auto; padding-top: 5px; }

.fe-option[b-4pkczubl49] {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    cursor: pointer;
    padding: 5px 8px;
    border-radius: 4px;
}

.fe-option:hover[b-4pkczubl49] { background: var(--hov); }

.fe-mark[b-4pkczubl49] {
    width: 14px;
    height: 14px;
    border: 1.5px solid var(--line3);
    border-radius: 3px;
    color: #FFFFFF;
    font-size: 10px;
    line-height: 11px;
    text-align: center;
    flex-shrink: 0;
    box-sizing: border-box;
}

/* An enum picks one, so its mark is a radio (`:158` — rad 50%); a multi picks many, so its
   mark is a box. The shape is the affordance; the fill is only the state. */
.fe-mark.round[b-4pkczubl49] { border-radius: 50%; }

.fe-option.on .fe-mark[b-4pkczubl49] { background: var(--btn); border-color: var(--btn); }

.fe-option-label[b-4pkczubl49] { font-size: 13px; color: var(--ink); }

.fe-caution[b-4pkczubl49] {
    margin: 9px 4px 0;
    background: var(--warnBg);
    border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
    border-radius: 6px;
    padding: 7px 9px;
    font-size: 12px;
    color: var(--warnInk);
    line-height: 1.45;
}

.fe-foot[b-4pkczubl49] {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 10px 2px 2px;
    padding-top: 9px;
    border-top: 1px solid var(--line4);
}

.fe-preview[b-4pkczubl49] {
    flex: 1;
    min-width: 0;
    font-size: 12px;
    color: var(--mut);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fe-add[b-4pkczubl49] {
    background: var(--btn);
    color: #FFFFFF;
    border: none;
    border-radius: 6px;
    height: 26px;
    padding: 0 12px;
    font: 600 13px 'Inter Tight', sans-serif;
    cursor: pointer;
    flex-shrink: 0;
}

.fe-add:hover:not(:disabled)[b-4pkczubl49] { background: var(--btnH); }

.fe-add:disabled[b-4pkczubl49] { background: var(--mutbg); color: var(--mut2); cursor: not-allowed; }
/* /Components/Screener/FilterMenu.razor.rz.scp.css */
/* Level 1 of the filter popover — `Cardstock Screener.dc.html:103–112` for the live rows and
   `Cardstock Charts.dc.html:145–162` for the locked form. The popover box itself (width, the
   card fill, the shadow) belongs to ScreenFilterRow; this file styles only what goes inside. */

.fm-group[b-vb6eiehsnw] {
    font: 600 11.5px 'Inter Tight', sans-serif;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mut2);
    padding: 8px 8px 3px;
}

.fm-row[b-vb6eiehsnw] {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    cursor: pointer;
    font: 13.5px 'Inter Tight', sans-serif;
    color: var(--ink);
    padding: 5px 8px;
    border-radius: 4px;
}

.fm-row:hover[b-vb6eiehsnw] { background: var(--hov); }

.fm-name[b-vb6eiehsnw] { flex: 1; }

/* The denominator badge (`:487` — beta's "24M MIN"): a caveat about the estimate, not a lock.
   The prototype's rgba(176,127,26,.12) is this app's --warnBg, which is the same hue at .10. */
.fm-badge[b-vb6eiehsnw],
.fm-lock[b-vb6eiehsnw] {
    font-size: 10.5px;
    font-weight: 600;
    padding: 1px 5px;
    border-radius: 3px;
    white-space: nowrap;
}

.fm-badge[b-vb6eiehsnw] { color: var(--warnInk); background: var(--warnBg); }

.fm-chev[b-vb6eiehsnw] { color: var(--line3); font-size: 13px; }

/* The locked form. `padding-left` matches the live rows' text rather than Charts' 34px glyph
   gutter, because this list has no glyph column; `opacity: .62` is Charts' own (:146). */
.fm-locked[b-vb6eiehsnw] { padding: 5px 8px; opacity: 0.62; }

.fm-locked-head[b-vb6eiehsnw] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
}

.fm-locked-name[b-vb6eiehsnw] {
    font-size: 13.5px;
    color: var(--mut);
    cursor: help;
    text-decoration: underline dotted rgba(138, 138, 134, 0.55);
    text-underline-offset: 3px;
}

.fm-lock[b-vb6eiehsnw] { color: var(--mut2); background: var(--mutbg); font-size: 11px; }

.fm-note[b-vb6eiehsnw] { font-size: 12px; color: var(--mut2); margin-top: 1px; }

.fm-prog[b-vb6eiehsnw] { display: flex; align-items: center; gap: 6px; margin-top: 4px; }

.fm-bar[b-vb6eiehsnw] {
    flex: 1;
    height: 3px;
    background: var(--line4);
    border-radius: 2px;
    overflow: hidden;
}

.fm-fill[b-vb6eiehsnw] { height: 100%; background: var(--warn); border-radius: 2px; }

.fm-prog-text[b-vb6eiehsnw] { font-size: 11px; color: var(--mut2); white-space: nowrap; }
/* /Components/Screener/HiddenRowsBanner.razor.rz.scp.css */
/* `Cardstock Screener.dc.html:380`: the strip is the table's own bottom edge — the same amber
   tint and border, `border-top: none` so the table's last rule is the only line between them,
   and only the bottom corners rounded. `width: max-content; min-width: 100%` is the prototype's
   own trick for a strip that has to stay as wide as a horizontally-scrolled table.

   The fill is --warnBg (tokens.css) rather than the prototype's raw rgba(176,127,26,.07): one
   definition of the tint, measured by ContrastTests against every hue that lands on it. */
.hidden-rows-banner[b-abc8rlnxbc] {
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  width: max-content;
  min-width: 100%;
  padding: 7px 12px;
  background: var(--warnBg);
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  border-top: none;
  border-radius: 0 0 10px 10px;
}

.hrb-text[b-abc8rlnxbc] {
  font-size: 12.5px;
  color: var(--warnInk);
}

/* :382 — a link, not a button, in everything but the element: it re-runs the query rather than
   navigating, so it stays a <button> for the keyboard and the screen reader. */
.hrb-toggle[b-abc8rlnxbc] {
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 12.5px;
  color: var(--acc);
  white-space: nowrap;
}

.hrb-toggle:hover[b-abc8rlnxbc] {
  text-decoration: underline;
}

.hrb-spacer[b-abc8rlnxbc] {
  flex: 1;
}

.hrb-rules[b-abc8rlnxbc] {
  font-size: 12px;
  color: var(--mut2);
  white-space: nowrap;
}
/* /Components/Screener/LeaveRunConfirm.razor.rz.scp.css */
/* D-291 ruling 10: Stay is the dialog's filled button and Leave and cancel its plain one, .btn-secondary, which
   app.css styles inside .modal-dialog. White on --btn is the app's primary pair (BacktestMode's .bt-run, app.css's
   .btn-primary), which ContrastTests' White_on_the_primary_button_clears_AA measures in all four modes; there is no
   token for white, and every one of those rules writes it as this one does. The box is .btn-delete-forever's
   (app.css), so a dialog's two buttons stand at one height. No hover rule: when this shipped, white on dark mode's
   --btnH failed AA. D-301 row 9 gave dark mode light mode's hover hex, and White_on_the_primary_button_hover_clears_AA
   now pins the pair in all four modes, so a hover is safe to add; none was ruled, and none is drawn. */
.leave-stay[b-a9sh74cdoa] { height: 30px; padding: 0 14px; border: none; border-radius: 6px; font-size: 14px; font-weight: 600; background: var(--btn); color: #FFFFFF; cursor: pointer; }
/* /Components/Screener/RulesChips.razor.rz.scp.css */
.rc[b-koqti2x03j] { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 4px; }
.rc-chip[b-koqti2x03j] { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; font-weight: 500; border-radius: 4px; padding: 1px 6px; white-space: nowrap; }
/* --neg on the negative tint and --pos on the positive one: ContrastTests pins both pairs over --card
   (Text_on_the_translucent_warn_fill_clears_AA). The tints are translucent and a group row in the activity log hovers
   to --hov, which put the bare tints under AA, so each is laid over a --card layer of its own and the rendered pair is
   the pinned one on any row background (TintOverCardCssTests holds the layer). */
.rc-chip.is-removed[b-koqti2x03j] { color: var(--neg); background: linear-gradient(var(--negBg), var(--negBg)), var(--card); text-decoration: line-through; }
.rc-chip.is-added[b-koqti2x03j] { color: var(--pos); background: linear-gradient(var(--posBg), var(--posBg)), var(--card); }
/* /Components/Screener/ScreenFilterRow.razor.rz.scp.css */
/* The filter row — `Cardstock Screener.dc.html:95–181`. */

.sfr[b-8z4rq0tjk5] {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: 8px;
}

.sfr-anchor[b-8z4rq0tjk5] { position: relative; }

.sfr-plus[b-8z4rq0tjk5] {
    background: var(--card);
    border: 1px dashed var(--line2);
    border-radius: 6px;
    height: 27px;
    padding: 0 10px;
    font-size: 13px;
    color: var(--mut);
    cursor: pointer;
}

.sfr-plus:hover[b-8z4rq0tjk5] { border-color: var(--acc); color: var(--acc); }

/* The outside-click catcher, as FilterPopover does it: an overlay rather than a document
   listener, so nothing has to be removed when the popover unmounts. Under the popover's
   z-index and above everything else. */
.sfr-overlay[b-8z4rq0tjk5] { position: fixed; inset: 0; z-index: 40; }

/* `:101` verbatim: the 380px cap and the box's own scroll, which level 1 needs (FilterMenu lists twenty-eight
   metric rows under seven group heads, far past 380px, and this scroll is what reaches them). */
.sfr-pop[b-8z4rq0tjk5] {
    position: absolute;
    left: 0;
    top: 31px;
    z-index: 50;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(20, 19, 26, 0.13);
    width: 300px;
    max-height: 380px;
    overflow-y: auto;
    padding: 6px;
}

/* D-301 row 134 (the owner, on the walk 2026-10-19): at the Set and Character picker's level the box lets go of
   its scroll. The picker's result list is the shared box's `.sb-menu` (SearchBox.razor.css), absolutely
   positioned below the input and scrolling on its own at 340px; under the box's `overflow-y: auto` it hung
   past the box's content, became part of the box's scroll area and showed about a row and a half. `visible`
   lets it hang below the box. The cap goes with the scroll: nothing at this level scrolls in the box any
   more, so 380px would only cut the box short of its own picked chips. The host sets the class
   (ScreenFilterRow.razor); every other level keeps the rule above. */
.sfr-pop.is-picking[b-8z4rq0tjk5] {
    max-height: none;
    overflow: visible;
}

.sfr-spacer[b-8z4rq0tjk5] { flex: 1; }

.sfr-matches[b-8z4rq0tjk5] {
    font-size: 12.5px;
    color: var(--mut);
    white-space: nowrap;
}

/* A run in flight dims the count it is replacing rather than blanking it. */
.sfr-matches[aria-busy="true"][b-8z4rq0tjk5] { opacity: 0.55; }
/* /Components/Screener/ScreenHeader.razor.rz.scp.css */
/* Cardstock Screener.dc.html:80–94. Baseline-aligned row, 10px gaps, and a flex spacer that
   keeps the name and thesis left while the row is full width. */
.screen-header[b-3w5vuphyvy] {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.spacer[b-3w5vuphyvy] {
  flex: 1;
}

/* :81 — 26×28, centred against a baseline-aligned row rather than sitting on the text
   baseline itself. */
.rail-toggle[b-3w5vuphyvy] {
  flex-shrink: 0;
  align-self: center;
  width: 28px;
  height: 26px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  color: var(--mut);
}

.rail-toggle:hover[b-3w5vuphyvy] {
  background: var(--hov);
  color: var(--ink);
}

/* :86 — the screen name, the page's one h1-weight line. */
.header-name[b-3w5vuphyvy] {
  font: 700 17px 'Inter Tight', sans-serif;
  color: var(--ink);
}

/* :83 — the inline edit keeps the name's exact type so nothing shifts as it opens, and the
   accent underline is the only chrome it adds. */
.edit-name[b-3w5vuphyvy] {
  width: 300px;
  padding: 0 2px;
  font: 700 17px 'Inter Tight', sans-serif;
  color: var(--ink);
  background: var(--card);
  border: none;
  border-bottom: 2px solid var(--acc);
  outline: none;
}

/* :92 — THESIS in small caps, then the thesis itself in italics. */
.thesis[b-3w5vuphyvy] {
  min-width: 0;
  font-size: 13px;
  color: var(--mut2);
}

.thesis-k[b-3w5vuphyvy] {
  margin-right: 5px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--mut2);
}

.thesis-v[b-3w5vuphyvy] {
  font-style: italic;
}

/* A screen with no thesis still says what the slot is for (D-087) rather than leaving a gap;
   --mut2 is the same hue the written thesis takes, so the placeholder does not read as a
   dimmer class of text than the thing it stands in for. */
.thesis-v.is-empty[b-3w5vuphyvy] {
  color: var(--mut2);
}

/* :89 — same type as the rendered thesis, wider box because a thesis is a sentence. */
.edit-thesis[b-3w5vuphyvy] {
  width: 420px;
  max-width: 100%;
  padding: 0 2px;
  font-family: inherit;
  font-size: 13px;
  font-style: italic;
  color: var(--mut);
  background: var(--card);
  border: none;
  border-bottom: 2px solid var(--acc);
  outline: none;
}
/* /Components/Screener/ScreenRail.razor.rz.scp.css */
/* Cardstock Screener.dc.html:53–73: 232px, --card ground, its own scroll, 14px/10px padding.
   The prototype's right rule does NOT survive, and this is a deliberate departure (D-236,
   owner UAT 2026-09-09). There the aside is flush to the viewport's left edge, where a square-cornered
   slab with one rule reads as a sidebar; <main> insets this one inside a max-width 1480px box,
   so it takes the app's own panel treatment instead -- the same edge and radius .screener-empty
   and .screener-error carry three inches to its right. `flex-shrink: 0` is what keeps the rail
   at 232px when the results table beside it wants more room than the row has. */
.screen-rail[b-rn1eicpc82] {
  width: 232px;
  flex-shrink: 0;
  box-sizing: border-box;
  overflow-y: auto;
  padding: 14px 10px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
}

/* :54 */
.rail-heading[b-rn1eicpc82] {
  padding: 0 6px 6px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mut2);
}

/* :56. `position: relative` is the containing block app.css's .band-row-menu-pop positions
   against (it is `position: absolute; top: 100%; right: 0`) — the band gets this from its own
   .band-row-menu wrapper, which is watchlist-row markup, not something to borrow here.
   Rounded on all four corners with no accent bar (D-241, owner UAT ruling) — a deliberate
   departure from the prototype's square-left-plus-bar; selection is carried by --accBg alone
   plus the active row's heavier name weight below, never by colour alone. */
.screen-row[b-rn1eicpc82] {
  position: relative;
  display: flex;
  align-items: stretch;
  border-radius: 5px;
}

.screen-row:hover[b-rn1eicpc82] {
  background: var(--hov);
}

/* :56's {{ s.bg }} — the accent tint fill, with no accent left edge (D-241). --accBg is the
   same fill the shipped FilterChip is drawn on (D-228 ruling 2), so the pairs this puts on it
   are --ink (the name) and --mut2 (the sub-line); ContrastTests pins all four modes. */
.screen-row.is-active[b-rn1eicpc82],
.screen-row.is-active:hover[b-rn1eicpc82] {
  background: var(--accBg);
}

/* :57–60 */
.screen-pick[b-rn1eicpc82] {
  flex: 1;
  min-width: 0;
  padding: 7px 8px;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
}

.screen-name[b-rn1eicpc82] {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}

.screen-row.is-active .screen-name[b-rn1eicpc82] {
  font-weight: 600;
}

.screen-sub[b-rn1eicpc82] {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  color: var(--mut2);
}

/* The ⋯ column keeps its own hit area the full height of the row (:61 is align-items: stretch
   on the row, so the button stretches); app.css's .band-actions-btn owns its hue and hover. */
.band-actions-btn[b-rn1eicpc82] {
  flex-shrink: 0;
  padding: 0 6px;
}

/* :73 — dashed, full width, left-aligned, and it accents on hover rather than filling. */
.rail-new[b-rn1eicpc82] {
  display: block;
  width: 100%;
  margin-top: 14px;
  padding: 8px;
  text-align: left;
  background: none;
  border: 1px dashed var(--line2);
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  color: var(--mut);
}

.rail-new:hover[b-rn1eicpc82] {
  border-color: var(--acc);
  color: var(--acc);
}

/* D-284 slice 3 §6: counts since the previous visit, each part beside its own glyph in its own hue. --pos, --neg and
   --warnInk on --card, and on the open row's --accBg — ContrastTests pins all six pairs. */
.screen-activity[b-rn1eicpc82] { display: flex; gap: 5px; align-self: center; flex-shrink: 0; padding-left: 4px; font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; white-space: nowrap; cursor: help; }
.screen-activity .is-entered[b-rn1eicpc82] { color: var(--pos); }
.screen-activity .is-exited[b-rn1eicpc82] { color: var(--neg); }
.screen-activity .is-rules[b-rn1eicpc82] { color: var(--warnInk); }
/* /Components/Screener/ScreenResults.razor.rz.scp.css */
/* `Cardstock Screener.dc.html:350–400`. The page is the scroller (D-221; ScreenerPage's
   .screener-results is a flex column, not an overflow box). This box is the hover preview's
   positioned ancestor, and its bottom edge is the preview's lower clamp bound (site.js's
   `bandBottom`).
   The first-load ring is the shared one in normal flow — no overlay rule here: the overlay
   this box used to carry sat under the sticky header (UAT finding 4, owner ruling B). The
   `min-height` predates that and was sized for the overlay; it is kept only because it also
   floors the clamp bound, and whether a one-row result needs it was not re-measured. */
.screen-results[b-drcu8hkstc] {
  position: relative;
  min-height: 120px;
}

/* --- The terminal table ------------------------------------------------------------------
   RosterTable is shared with Set and Character (the Binder's two tables and
   the activity log copy its look). Its header sticks under the 48px nav and wears the app's table
   face (app.css's `--table-head-*`, D-298 UAT walk 2026-10-08, finding 2 -- the face D-234 walk finding 3 gave
   this page, now every table's). What stays here is the Screener's own: its grip's accent hover,
   and rows 78px instead of 30: 5 + 66 (the thumbnail's height, unchanged since the mockup, :361
   -- CardCell owns it now, D-236) + 6 + a 1px rule. */
.screen-results[b-drcu8hkstc]  .rt-grip:hover {
  color: var(--acc);
}

.screen-results[b-drcu8hkstc]  .rt-row {
  padding-top: 5px;
  padding-bottom: 6px;
}

/* The banner is welded to the table's bottom edge (:380), so the table gives up its own two
   bottom corners and its bottom border to it. Both are restored when nothing is hidden. Its last row keeps the
   rule that parts it from the banner (`--rt-foot-rule`, .rt-row's own, RosterTable.razor.css). */
.screen-results.has-banner[b-drcu8hkstc]  .roster-table {
  --rt-foot-radius: 0;
  --rt-foot-rule: 1px solid var(--line4);
  border-bottom: none;
  border-radius: 10px 10px 0 0;
}

/* The pager's own weld to the table (border-bottom/radius on .roster-table, border on .pg-bar)
   moved to app.css's `.roster-paged` (D-236) now that Browse's two rosters want it too. What
   stays here is Screener-only: with the hidden-rows banner present, the pager gives up its own
   foot -- the banner welds under whichever of the table or the pager is the bottom edge. */
.screen-results.has-banner[b-drcu8hkstc]  .pg-bar {
  border-bottom: none;
  border-radius: 0;
}

/* :364–366 — the one left-aligned column on the row: a card's name is not a metric to centre
   like the mono columns beside it. `CardCell` (D-236) owns the 48x66 thumbnail, its
   deterministic per-card tint and both text lines now — this rule only keeps the column itself
   left-aligned. */
.screen-results[b-drcu8hkstc]  .rt-cell-card {
  text-align: left;
  min-width: 0;
}

/* :367–368 — price heavier and larger than the metric columns beside it. */
.screen-results[b-drcu8hkstc]  .rt-cell-price {
  font: 700 13.5px 'JetBrains Mono', monospace;
  color: var(--ink);
}

.screen-results[b-drcu8hkstc]  .pos { color: var(--pos); }
.screen-results[b-drcu8hkstc]  .neg { color: var(--neg); }

/* :505-507 — the + Binder button on the row's action track. Its dress is the shipped `.pg-btn`'s
   (Pager.razor.css), which is the button welded under this very table when the result set pages,
   and the nearest thing in production that means the same: a small secondary control on --card
   inside a --line hairline, 24px tall, 5px radius, --ink text, all six taken from there. Three
   declarations are not its: `padding: 0 8px` and `white-space: nowrap` are the mockup's (its
   `0 9px` and no wrap), and the font is the table header's (`--table-head-font`, app.css),
   which is also the mockup's. The hover is the
   mockup's, and --acc on hover is what the grip in this same file already takes. A new class
   rather than a reuse: `.pg-btn` is Pager's own CSS-isolated class and nothing outside that
   component can name it. */
.sr-add[b-drcu8hkstc] {
  height: 24px;
  padding: 0 8px;
  border-radius: 5px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  font: var(--table-head-font);
  white-space: nowrap;
  cursor: pointer;
}

.sr-add:hover[b-drcu8hkstc] {
  border-color: var(--acc);
  color: var(--acc);
}

/* --- Binder density (:391–401) ----------------------------------------------------------- */
.sr-tile-name[b-drcu8hkstc] {
  font: 600 13.5px 'Inter Tight', sans-serif;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The tier line the mockup's gallery tile does not have. A screener row is a (card, tier)
   pair, and two tiers of one card can both match -- without it the wall shows the same name
   twice with two different prices and nothing to explain it. */
.sr-tile-sub[b-drcu8hkstc] {
  margin-top: 1px;
  font: 400 11.5px 'Inter Tight', sans-serif;
  color: var(--mut2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sr-tile-stats[b-drcu8hkstc] {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 3px;
}

.sr-tile-price[b-drcu8hkstc] {
  font: 700 13.5px 'JetBrains Mono', monospace;
  color: var(--ink);
}

.sr-tile-roc[b-drcu8hkstc] {
  font: 500 12px 'JetBrains Mono', monospace;
  color: var(--mut);
}

.sr-tile-roc.pos[b-drcu8hkstc] { color: var(--pos); }
.sr-tile-roc.neg[b-drcu8hkstc] { color: var(--neg); }

.sr-truncated[b-drcu8hkstc] {
  margin: 10px 2px 0;
  font-size: 12.5px;
  color: var(--mut2);
}

/* --- Empty and failed --------------------------------------------------------------------
   A panel on its own is a full box: "No filters yet" and binder density's "No matches" have
   nothing above them. Both were drawn with the welded form below and so had no top edge at
   all (Screener A UAT finding 5, the owner's screenshot). */
.sr-empty[b-drcu8hkstc] {
  padding: 24px 16px;
  text-align: center;
  font-size: 13px;
  color: var(--mut2);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
}

/* Terminal density keeps the header row above an empty result (§6): the columns are what the
   result set would have filled, and stripping them leaves a bare sentence with no evidence of
   what was asked. That panel welds to the header — no top edge of its own, square top corners. */
.sr-empty.is-welded[b-drcu8hkstc] {
  border-top: none;
  border-radius: 0 0 10px 10px;
}

/* The app-wide load-failed look (app.css, D-213), written here for the same reason
   ScreenerPage writes its own: a scoped rule outranks the global one. */
.sr-error[b-drcu8hkstc] {
  padding: 20px;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--neg);
  border-radius: 10px;
  color: var(--neg);
}

.sr-error p[b-drcu8hkstc] {
  margin: 0 0 10px;
}

.sr-error button[b-drcu8hkstc] {
  color: var(--ink);
}

/* Binder density has no table edge for the strip to weld to, so it takes its own box back:
   the top border and the two top corners the terminal table lends it. */
.screen-results.is-binder[b-drcu8hkstc]  .hidden-rows-banner {
  margin-top: 4px;
  border-top: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  border-radius: 8px;
}
/* /Components/SearchBox.razor.rz.scp.css */
.sb[b-356hnrywpc] { position: relative; }
.sb-nav[b-356hnrywpc] { width: 280px; height: 30px; }                       /* §2.1 host box */
.sb-input[b-356hnrywpc] { width: 100%; height: 30px; box-sizing: border-box; padding: 0 30px 0 10px; border: 1px solid var(--line); border-radius: 6px; background: var(--inbg); color: var(--ink); font-size: 15px; }
.sb-pick .sb-input[b-356hnrywpc] { height: auto; padding: 6px 8px; background: var(--card); font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; }
.sb-input[b-356hnrywpc]::placeholder { color: var(--mut2); }
/* owner ruling, 2026-08-31: the native type=search clear button (tinted by the app's global
   accent-color) is off-design -- Escape is the only clearing contract this component offers. */
.sb-input[b-356hnrywpc]::-webkit-search-cancel-button, .sb-input[b-356hnrywpc]::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
.sb-input.is-linked[b-356hnrywpc] { border-color: rgba(21, 122, 80, 0.45); }
.sb-kbd[b-356hnrywpc] { position: absolute; right: 8px; top: 6px; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; color: var(--mut2); border: 1px solid var(--line); border-radius: 4px; padding: 0 5px; background: var(--card); pointer-events: none; }
/* §2.5: bleeds 60px past the input's right edge, z-index 80, max-height 340px.
   overscroll-behavior: contain -- owner ruling, 2026-08-31 (Search UAT finding 7): a wheel
   over the open menu used to chain to the scroll parent once the menu had nothing left to
   scroll (an expanded list at its end, or a short list that fits the box); now that parent
   holds still under an open menu -- the page in the nav, the modal panel in Pick. */
.sb-menu[b-356hnrywpc] { position: absolute; top: 36px; left: 0; right: -60px; z-index: 80; background: var(--card); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 10px 28px rgba(20, 19, 26, 0.14); padding: 5px; max-height: 340px; overflow-y: auto; overscroll-behavior: contain; }
.sb-pick .sb-menu[b-356hnrywpc] { right: 0; }                               /* inside a 400px modal, no bleed */
.sb-grp[b-356hnrywpc] { font-size: 10.5px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--mut2); padding: 6px 8px 2px; }
.sb-item[b-356hnrywpc] { display: flex; align-items: baseline; gap: 8px; width: 100%; box-sizing: border-box; border: 0; background: none; text-align: left; cursor: pointer; border-radius: 5px; padding: 5px 8px; font-size: 13.5px; color: var(--ink); text-decoration: none; }
.sb-item:hover[b-356hnrywpc], .sb-item.sb-hl[b-356hnrywpc] { background: var(--hov); }
.sb-name[b-356hnrywpc] { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }   /* truncates second, after .sb-sub */
.sb-num[b-356hnrywpc] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12px; color: var(--ink); white-space: nowrap; }   /* never truncates (spec §5) */
.sb-sub[b-356hnrywpc] { font-size: 12px; color: var(--mut2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }        /* the set truncates first */
.sb-none[b-356hnrywpc], .sb-more[b-356hnrywpc] { font-size: 12.5px; color: var(--mut2); padding: 6px 8px; }
.sb-more[b-356hnrywpc] { border-top: 1px solid var(--line); margin-top: 4px; }
/* D-215: below the API ceiling, .sb-more renders as a <button> that expands its group --
   reset the UA button chrome, then match .sb-item's cursor/hover exactly. Family and
   line-height are reset one property at a time, NOT with the `font:` shorthand: the
   shorthand also sets font-size to the menu's inherited 15px, and this element+class
   selector outranks .sb-more's 12.5px -- the clickable footer rendered 15px beside the
   static footer's 12.5px (owner, Search UAT 2026-08-31; measured in headless Chrome,
   15px → 12.5px with this change). .sb-more's own font-size beats the UA button size on
   its own, being an author rule. The same trap once more with `border: 0`: it also zeroed
   .sb-more's top rule, so the button footer sat rule-less beside the static one -- the top
   rule is restored right after the reset (review of 1c9d62f, measured 0px → 1px). */
button.sb-more[b-356hnrywpc] { display: block; width: 100%; border: 0; border-top: 1px solid var(--line); background: none; text-align: left; font-family: inherit; line-height: inherit; cursor: pointer; }
button.sb-more:hover[b-356hnrywpc], button.sb-more.sb-hl[b-356hnrywpc] { background: var(--hov); }
/* /Components/Settings/AccountCard.razor.rz.scp.css */
/* profile.md §2.1/§4.3-4.5 (Cardstock Profile.dc.html:117-175): three justified rows
   (title/value left, a 30px secondary button right); rows 2 and 3 carry border-top. The
   shared card shell (.settings-card/h2) lives in SettingsPage.razor.css and reaches in via
   ::deep, so only Account's own internals are declared here. */
.account-row[b-rwcz42a3bu] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    padding: 10px 0;
}

.account-row.bordered[b-rwcz42a3bu] {
    border-top: 1px solid var(--line);
}

/* :169 -- the last row's own padding is 10px 0 2px, not the other two rows' 10px 0. */
.account-row.session-row[b-rwcz42a3bu] {
    padding-bottom: 2px;
}

.row-title[b-rwcz42a3bu] {
    font-weight: 600;
}

.row-value[b-rwcz42a3bu] {
    font-size: 13.5px;
    color: var(--mut);
    margin-top: 2px;
}

/* :120 -- the email value is the one row-value that's JetBrains Mono, its own font-size
   (13px) overriding the shared 13.5px, matching ProfileCard's identical .mono override. */
.row-value.mono[b-rwcz42a3bu] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 13px;
}

.pw-flash[b-rwcz42a3bu] {
    color: var(--pos);
}

/* :126, :139, :153 -- panels drop in BELOW their row, a bordered var(--bg) box. */
.account-panel[b-rwcz42a3bu] {
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 12px 14px;
    margin-bottom: 10px;
    background: var(--bg);
}

.account-panel .field-label[b-rwcz42a3bu] {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--mut);
    margin: 12px 0 5px;
}

/* :129, :156 -- each panel's FIRST label carries 0 top margin; every later label in the
   same flat panel (no per-field wrapper divs, matching the prototype's own structure) gets
   the 12px top margin above. */
.account-panel .field-label:first-child[b-rwcz42a3bu] {
    margin-top: 0;
}

/* :129/:131/:156/:158/:161 -- the Account section's own panel inputs sit on var(--card),
   NOT var(--inbg) the way Profile's fields do (ProfileCard.razor.css) -- verified directly
   against the frozen prototype rather than assumed symmetric with the other card. */
.account-panel .field-input[b-rwcz42a3bu] {
    width: 100%;
    box-sizing: border-box;
    height: 34px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--card);
    padding: 0 10px;
    font-size: 15px;
    color: var(--ink);
}

.account-panel .field-helper[b-rwcz42a3bu] {
    font-size: 12.5px;
    color: var(--mut2);
    margin-top: 8px;
}

.error-line[b-rwcz42a3bu] {
    margin-top: 8px;
}

.panel-actions[b-rwcz42a3bu] {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

/* :139-144 -- the pending banner: warnBg fill, warn text, a compact 26px Cancel. */
.pending-banner[b-rwcz42a3bu] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 10px 14px;
    margin-bottom: 10px;
    background: var(--warnBg);
}

.pending-text[b-rwcz42a3bu] {
    font-size: 13.5px;
    color: var(--warn);
}

.btn-cancel-compact[b-rwcz42a3bu] {
    height: 26px;
    padding: 0 10px;
    background: transparent;
    color: var(--mut);
    border: 1px solid var(--line);
    border-radius: 6px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    flex-shrink: 0;
}

.btn-cancel-compact:hover[b-rwcz42a3bu] {
    background: var(--hov);
}
/* /Components/Settings/AppearanceCard.razor.rz.scp.css */
/* profile.md §2.1: two justified rows (label left, control right), the second separated by
   a border-top + margin-top 10px, then the preview strip. The shared card shell
   (.settings-card / h2) lives in SettingsPage.razor.css and reaches in via ::deep. */
.appearance-row[b-8e8kxbof89] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 0;
}

.appearance-row.bordered[b-8e8kxbof89] {
    padding: 12px 0 6px;
    border-top: 1px solid var(--line);
    margin-top: 10px;
}

.row-title[b-8e8kxbof89] {
    font-weight: 600;
}

.row-sub[b-8e8kxbof89] {
    font-size: 12.5px;
    color: var(--mut2);
    margin-top: 2px;
}

/* profile.md §4.1: selected half is var(--btn) on white, unselected transparent on
   var(--mut) -- exhaustive, exactly one of Light/Dark selected. */
.theme-segmented[b-8e8kxbof89] {
    display: flex;
    border: 1px solid var(--line);
    border-radius: 6px;
    overflow: hidden;
}

.theme-segmented button[b-8e8kxbof89] {
    height: 30px;
    padding: 0 14px;
    border: none;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
    background: transparent;
    color: var(--mut);
}

.theme-segmented .theme-dark[b-8e8kxbof89] {
    border-left: 1px solid var(--line);
}

.theme-segmented button.active[b-8e8kxbof89] {
    background: var(--btn);
    color: #FFFFFF;
}

.preview[b-8e8kxbof89] {
    margin-top: 14px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--bg);
    padding: 12px 14px;
}

.preview-label[b-8e8kxbof89] {
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--mut2);
    text-transform: uppercase;
    margin-bottom: 8px;
}

.preview-chips[b-8e8kxbof89] {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.chip[b-8e8kxbof89] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 4px;
}

.chip-pos[b-8e8kxbof89] {
    color: var(--pos);
    background: var(--posBg);
}

.chip-neg[b-8e8kxbof89] {
    color: var(--neg);
    background: var(--negBg);
}

.chip-warn[b-8e8kxbof89] {
    color: var(--warn);
    background: var(--warnBg);
}

.chip-muted[b-8e8kxbof89] {
    color: var(--mut2);
    background: var(--mutbg);
}

.pct[b-8e8kxbof89] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 14px;
    font-weight: 600;
}

.pct-pos[b-8e8kxbof89] {
    color: var(--pos);
    margin-left: 10px;
}

.pct-neg[b-8e8kxbof89] {
    color: var(--neg);
}
/* /Components/Settings/DangerZone.razor.rz.scp.css */
/* profile.md §2.1/§4.6: one justified row, explanatory copy left, an outlined red button
   right (flex-shrink: 0). The shared card shell (.settings-card / h2 font+margin) lives in
   SettingsPage.razor.css and reaches in via ::deep; this file only adds the h2's own color
   (the one colored heading on the page, §2 item 5) plus this card's internals. The shared
   rule's specificity (.settings-container ::deep .settings-card h2, three selectors) beats a
   plain ".danger-card h2" (two selectors) for any property both declare -- harmless here
   since only color is declared below and the shared rule never sets color, but it's why this
   file doesn't also chase the spec's "Account and Danger zone use margin 0 0 6px" (profile.md
   §2 item 5): AccountCard.razor.css doesn't either, for the identical reason, and overriding
   only one of the two cards would leave them inconsistent with EACH OTHER instead of both
   matching the spec. */
.danger-card h2[b-7b4a1vf212] {
    color: var(--neg);
}

.danger-row[b-7b4a1vf212] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
}

.danger-copy[b-7b4a1vf212] {
    font-size: 13.5px;
    color: var(--mut);
    line-height: 1.5;
}

.btn-delete-outline[b-7b4a1vf212] {
    height: 30px;
    padding: 0 12px;
    background: var(--card);
    color: var(--neg);
    border: 1px solid var(--neg);
    border-radius: 6px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    flex-shrink: 0;
}

.btn-delete-outline:hover[b-7b4a1vf212] {
    background: var(--negBg);
}

/* profile.md §2 item 5 / OQ-12: fixed inset:0 scrim, flex-centered, padding 16; a 420px
   dialog (max-width 100% for narrow viewports) with the same card chrome used elsewhere.
   Deliberately no @onclick here (see the .razor's own comment) -- OQ-12 asks for a focus
   trap and Escape, not scrim-click-to-dismiss, and this is the app's most destructive
   control. `.modal-scrim`/`.modal-dialog`/`.modal-dialog h3`/`.modal-body`/`.modal-actions`/
   `.error-line`/`.btn-secondary`/`.btn-delete-forever` moved to app.css 2026-08-28 (its
   "Shared modal frame" comment) so Task 4's binder delete modal reuses this frame instead
   of copying it; the elements below still resolve to the identical rules via app.css's
   global stylesheet plus, for `.error-line`/`.btn-secondary`, its `.modal-dialog`-scoped
   selector reaching this component's own `#delete-modal`. */

/* Type, colour and the input's box come from app.css; only the stacking gap is local. */
.field-label[b-7b4a1vf212] {
    margin: 14px 0 5px;
}

/* prototype :193 -- the confirm box is the one field on this whole page set in JetBrains
   Mono, 14px (every other field-input on Profile is Inter 15px). */
.field-input.mono[b-7b4a1vf212] {
    font-family: 'JetBrains Mono', monospace;
    font-size: 14px;
}

/* D-301 row 133 (owner, 2026-10-19): the delete confirm's export link had no rule, so it drew in the
   browser's default blue -- hard to read on the modal's dark ground. It takes the app's link tokens
   (tokens.css: --link, --link-hover), underlined because it sits inside a sentence of prose. Both clear AA
   on --card in all four modes: ContrastTests.The_text_hues_clear_AA_on_every_surface's --link and
   --link-hover rows. */
.delete-export-link[b-7b4a1vf212] {
    color: var(--link);
    text-decoration: underline;
}

.delete-export-link:hover[b-7b4a1vf212] {
    color: var(--link-hover);
}
/* /Components/Settings/ProfileCard.razor.rz.scp.css */
/* profile.md §2.1: 72x72 avatar-slot + a 1fr/1fr fields grid, gap 20px between them. The
   shared card shell (.settings-card / h2) lives in SettingsPage.razor.css and reaches in
   via ::deep, so only Profile's own internals are declared here. */
.profile-row[b-uin6wi2b8a] {
    display: flex;
    gap: 20px;
    align-items: flex-start;
}

/* DeferredControl renders <button class="deferred avatar-slot">, so ::deep is required to
   reach through that child component's own template (AppChrome.razor.css's ".tabs ::deep
   .tab" is the established precedent for this). app.css's global .deferred rule already
   supplies the muted color + not-allowed cursor -- exactly the D-087 "deferred" look this
   avatar is spec'd to have -- so no color override belongs here. */
.profile-row[b-uin6wi2b8a]  .avatar-slot {
    width: 72px;
    height: 72px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--mutbg);
    border: 1px solid var(--line);
    font-size: 26px;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

.profile-fields[b-uin6wi2b8a] {
    flex: 1;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 16px;
}

/* Type, colour and the input's box are app.css's. The grid above already spaces the rows,
   so a label here carries no top margin -- the only local difference. */
.field-label[b-uin6wi2b8a] {
    margin: 0 0 5px;
}

/* prototype :60 -- the timezone select carries 0 6px padding, not the input's 0 10px. */
select.field-input[b-uin6wi2b8a] {
    padding: 0 6px;
}

.field-helper[b-uin6wi2b8a] {
    margin-top: 5px;
}

.profile-actions[b-uin6wi2b8a] {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
}

.btn-save[b-uin6wi2b8a] {
    height: 30px;
    padding: 0 14px;
    background: var(--btn);
    color: #FFFFFF;
    border: none;
    border-radius: 6px;
    font-size: 14.5px;
    font-weight: 600;
    cursor: pointer;
}

.btn-save:hover[b-uin6wi2b8a] {
    background: var(--btnH);
}

.btn-save:disabled[b-uin6wi2b8a] {
    cursor: not-allowed;
}

.saved-flash[b-uin6wi2b8a] {
    font-size: 13.5px;
    color: var(--pos);
}

/* `.error-line` itself is app.css's. This one sits inline in the actions row, so unlike the
   two Settings panels it takes no top margin. */
/* /Components/Settings/StricterMeasuresCard.razor.rz.scp.css */
/* D-296 spec §4.8: AppearanceCard's row shape (a justified row, title and sentence left, the switch right) under a
   one-line lead. The shared card shell (.settings-card / h2) lives in SettingsPage.razor.css and reaches in via
   ::deep. Text pairs are ones ContrastTests already pins: --mut and --mut2 on --card. */
.stricter-lead[b-n75ulstgvp] {
    margin: 0 0 10px;
    font-size: 13.5px;
    color: var(--mut);
    line-height: 1.5;
}

.stricter-row[b-n75ulstgvp] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    padding: 12px 0 6px;
    border-top: 1px solid var(--line);
}

.row-title[b-n75ulstgvp] {
    font-weight: 600;
}

.row-sub[b-n75ulstgvp] {
    font-size: 12.5px;
    color: var(--mut2);
    margin-top: 2px;
}

/* The look is app.css's .error-line (ErrorLookTests.ErrorSurfaces); this only spaces it under the sentence, as
   AccountCard.razor.css does. */
.error-line[b-n75ulstgvp] {
    margin-top: 8px;
}
/* /Components/ToggleSwitch.razor.rz.scp.css */
/* brand.md §4 "Toggle switch": 36x20 track, 16px knob, translateX(16px) on, 0.15s transition — moved here from
   AppearanceCard.razor.css (D-296 spec §4.8), where the colourblind switch defined it. The on state is that switch's,
   unchanged: --btn under a white knob (White_on_the_primary_button_clears_AA's pair).

   Ruling 11 fixes the off state. The track keeps its --line fill — the prototype's live colour (Cardstock
   Profile.dc.html:216–217; D-136 (l), in D-130's frame; brand.md §9 row 14) — but --line on --card measures 1.27:1,
   under D-145's 3:1 for a control's state, so the track gains a 1px --mut3 edge (3.25:1 light, 5.90 dark on --card) and
   the knob turns --mut2 (4.20:1 light, 5.31 dark on --line). ContrastTests.The_switch_state_clears_the_non_text_bar
   pins the three pairs in all four modes. The edge is an inset shadow so the switch's box does not grow by a pixel. */
.toggle-switch[b-e2zokn3mqs] {
    width: 36px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    position: relative;
    flex-shrink: 0;
    background: var(--line);
    box-shadow: inset 0 0 0 1px var(--mut3);
}

.toggle-switch[aria-checked="true"][b-e2zokn3mqs] {
    background: var(--btn);
    box-shadow: none;
}

/* Busy: the save is out. Not dimmed — the state shown is the one being saved. */
.toggle-switch[aria-disabled="true"][b-e2zokn3mqs] {
    cursor: progress;
}

.toggle-knob[b-e2zokn3mqs] {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--mut2);
    transition: transform 0.15s;
    transform: none;
}

.toggle-switch[aria-checked="true"] .toggle-knob[b-e2zokn3mqs] {
    /* White on the accent. No token names white, so it is written as a hex, as AppearanceCard's selected theme button
       (.theme-segmented button.active), LeaveRunConfirm's .leave-stay and BacktestMode's .bt-run already write it. */
    background: #FFFFFF;
    transform: translateX(16px);
}
/* /Layout/AppChrome.razor.rz.scp.css */
/* card.md §1 Chrome: sticky, top:0, z-index:20, height 48px, --card background,
   1px --line bottom border, 24px gap, 20px horizontal padding. */
.app-chrome[b-j9qiqpqid8] {
    position: sticky;
    top: 0;
    z-index: 20;
    height: 48px;
    background: var(--card);
    border-bottom: 1px solid var(--line);
    display: flex;
    align-items: center;
    gap: 24px;
    padding: 0 20px;
}

/* card.md §3.4 wordmark lockups — app nav: mark 24px + wordmark 18px, gap 10px. */
/* shared-components.md §1.3: the anchor is `color: inherit; text-decoration: none`, overriding
   the global accent-and-underline an <a> would otherwise take — the lockup must read as the
   brand mark, not as a link. */
.logo-lockup[b-j9qiqpqid8] {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
    color: inherit;
    text-decoration: none;
}

/* The wordmark is AppLogo's markup and face (AppLogo.razor.css); the app bar sets its size. */
.logo-lockup[b-j9qiqpqid8]  .wordmark {
    font-size: 18px;
}

.tabs[b-j9qiqpqid8] {
    display: flex;
    align-items: center;
    gap: 24px;
    height: 100%;
}

/* Base tab treatment, shared by the four still-deferred buttons and the live Browse
   anchor: border-bottom: 2px solid transparent until .active overrides it below. Resting
   color is --mut, the same muted-control convention DensityToggle's resting buttons use
   (DensityToggle.razor.css:12); text-decoration: none strips the browser-default anchor
   underline (buttons never had one, so that line is inert for the deferred four). app.css
   has no global `a` reset, so without this the live Browse anchor would render
   browser-default blue-and-underlined whenever it isn't active. The deferred four also
   carry app.css's .deferred utility, which sets the identical color: var(--mut) -- same
   value however the cascade orders the two rules, so no specificity fix was needed. The
   Card screen never lights Browse active either (card.md:41 -- a leaf, not a tab). */
.tabs[b-j9qiqpqid8]  .tab {
    /* height:100% centers nothing by itself: a <button> vertically centers its label
       via UA button layout, but the live <a> top-aligns its line box inside the
       stretched box (label rode ~11px high). inline-flex centers both element types
       by one mechanism. */
    display: inline-flex;
    align-items: center;
    height: 100%;
    padding: 0 2px;
    font-size: 15px;
    color: var(--mut);
    text-decoration: none;
    border-bottom: 2px solid transparent;
}

/* home.md:23 / set.md:37 / character.md:40 / shared-components.md:66-68 — Browse lights
   active on /browse and on its descendant catalog routes /set/* and /character/*. */
.tabs[b-j9qiqpqid8]  .tab.active {
    font-weight: 600;
    color: var(--ink);
    border-bottom: 2px solid var(--acc);
}

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

/* charts.md:42 — the prototype's own nav-search flex, so the box shrinks before the tabs
   or the account avatar do on a narrow viewport. */
.app-chrome[b-j9qiqpqid8]  .sb-nav {
    flex: 0 1 280px;
    min-width: 110px;
}

/* --mutbg's documented role (brand.md §2.2) is "Neutral chip / avatar fill". Explicit
   color + text-decoration: app.css has no global `a` reset (Breadcrumb.razor.css's
   .crumb-link comment and this file's own .tab comment both document the same gap), so the
   live signed-in/signed-out anchor would otherwise render UA blue-and-underlined instead of
   the muted resting tone the mockup gives it (shared-components.md:79-83, Home:53). */
.app-chrome[b-j9qiqpqid8]  .avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--mutbg);
    border: 1px solid var(--line);
    color: var(--mut);
    font-size: 12.5px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    text-decoration: none;
}

/* shared-components.md §1.6 "Current-page variant": on /settings itself the circle is an
   inert accent-bordered span -- same geometry, accent-coloured, no href (Profile:40). */
.app-chrome[b-j9qiqpqid8]  .avatar.current {
    border-color: var(--acc);
    color: var(--acc);
}
/* /Layout/AuthLayout.razor.rz.scp.css */
/* account.md §2 item 1 -- page frame: full-viewport, centered both axes. */
.auth-frame[b-m8xwx0d3tt] {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40px 16px;
    background: var(--bg);
    color: var(--ink);
}

/* account.md §2 item 2 -- brand lockup: 32px mark + wordmark, sub-line below. */
.brand-lockup[b-m8xwx0d3tt] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    margin-bottom: 22px;
}

.brand-row[b-m8xwx0d3tt] {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* The logo takes whatever background is behind it (owner, D-293): AppLogo fills its front card
   from --card, and this page's ground is --bg, so the mark alone re-points it. ::deep, because the
   <svg> is AppLogo's markup. Scoped to the mark so no text inherits the value. */
.brand-row[b-m8xwx0d3tt]  svg {
    --card: var(--bg);
}

/* The wordmark is AppLogo's markup and face (AppLogo.razor.css); the signed-out shell sets its size. */
.brand-row[b-m8xwx0d3tt]  .wordmark {
    font-size: 25px;
}

.sub-line[b-m8xwx0d3tt] {
    font-size: 14px;
    color: var(--mut2);
}

/* account.md §2 item 4 -- legal footnote, below the card. */
.legal-footnote[b-m8xwx0d3tt] {
    font-size: 12.5px;
    color: var(--mut2);
    margin-top: 16px;
}
/* /Layout/MainLayout.razor.rz.scp.css */
/* card.md §2: max-width 1480px, centred, padding 14px 20px 28px, vertical flex, gap 16px. */
main[b-p923pucyv5] {
    max-width: 1480px;
    margin: 0 auto;
    padding: 14px 20px 28px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    /* D-221: <main> takes the height left over by the chrome so the footer rests on the
       bottom edge instead of floating wherever the content ran out — most visible while a
       page is still loading, which used to leave the footer stranded under the nav. The
       flex parent is #app (app.css); `width: 100%` is load-bearing, because `margin: 0 auto`
       on a flex item absorbs the free cross-axis space and would otherwise shrink this box
       to its content instead of stretching it to the 1480px cap. */
    flex: 1 0 auto;
    width: 100%;
    box-sizing: border-box;
}
/* /Pages/AccountPage.razor.rz.scp.css */
/* account.md §2 item 3 -- the 380px card. Every view renders inside this one
   wrapper; it never changes size or chrome between views (account.md:70). */
.account-card[b-be0ltomtau] {
    width: 380px;
    max-width: 100%;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 22px;
}

/* account.md §2.1 -- banner (flash/error), above the h1. Rule 6: one card-level
   banner, no field-level error styling. Rule 7: success suppresses error. */
.banner[b-be0ltomtau] {
    font-size: 13.5px;
    border-radius: 6px;
    padding: 8px 10px;
    margin-bottom: 14px;
}

.banner-success[b-be0ltomtau] {
    color: var(--pos);
    background: var(--posBg);
}

/* The app-wide error look (app.css, D-213): --neg text and a 3px --neg left rule, no glyph.
   Written here because a scoped `.banner-error[b-…]` outranks the global rule; .banner's own
   10px left padding already clears the bar. .banner-success is not a failure and keeps its
   plain tinted block. */
.banner-error[b-be0ltomtau] {
    color: var(--neg);
    background: var(--negBg);
    border-left: 3px solid var(--neg);
}

.banner-link[b-be0ltomtau] {
    margin-left: 4px;
}

/* account.md §2.1 -- view heading. app.css already sets h1 to Inter Tight 700,
   margin 0 for every page; only the size is specific to this screen. */
h1[b-be0ltomtau] {
    font-size: 19.5px;
}

/* account.md §2.1 -- sub-copy under the heading: forgot's intro line, sent's
   mailed-link line, reset's "you followed a link" context line. */
.sub-copy[b-be0ltomtau] {
    font-size: 13.5px;
    color: var(--mut);
    margin-top: 8px;
    line-height: 1.5;
}

/* account.md §2.1 -- masked email (JetBrains Mono via app.css's global .mono
   utility, which carries no font-size of its own). account.md:91 and the frozen
   prototype both specify 13px for this span, not the 13.5px it would otherwise
   inherit from its .sub-copy parent (Task 16 -> 17 parked finding). */
.mono[b-be0ltomtau] {
    font-size: 13px;
}

/* account.md §2.1 -- field label and its row. Type and colour are app.css's; the row's own
   layout is Field.razor.css's. What is left is this surface's stacking rhythm, and it needs
   ::deep because <Field> authors that markup now -- a scoped rule only reaches elements its
   own component rendered. */
.account-card[b-be0ltomtau]  .field-label {
    margin: 14px 0 5px;
}

.account-card[b-be0ltomtau]  .field-row {
    margin: 14px 0 5px;
}

.account-card[b-be0ltomtau]  .field-row .field-label {
    margin: 0;
}

/* The "Forgot?" button is authored here, not in Field, so it needs no ::deep -- Blazor scopes
   only the last compound selector, and that element carries this page's scope. */
.field-row .inline-link[b-be0ltomtau] {
    font-size: 13px;
}

/* account.md §2.1 -- the text input itself is app.css's `.field-input`, identical across
   every view here and on all three Settings cards. */

/* account.md §2.1 -- helper text under an input (the shared 12+ password rule); only the
   gap above it is this surface's, and <Field> renders it, hence ::deep. */
.account-card[b-be0ltomtau]  .field-helper {
    margin-top: 5px;
}

/* account.md §2.1 -- primary/secondary buttons share one box here: full width, 34px, 15px. Their
   fill, text colour and hover are app.css's .btn-primary / .btn-secondary, the app's one button
   look (D-301 row 46); this rule's two classes outrank those on the box. ::deep reaches
   DeferredControl's own <button> (the demo control) the same way AppChrome.razor.css's
   ".tabs ::deep .tab" reaches its deferred tabs -- ::deep drops the scope requirement on the
   right-hand selector, so it matches both this file's own buttons and the child's. */
.account-card[b-be0ltomtau]  .btn {
    width: 100%;
    height: 34px;
    margin-top: 18px;
    border-radius: 6px;
    font-size: 15px;
    font-weight: 600;
}

/* account.md §2.1 -- the "or" divider. */
.divider[b-be0ltomtau] {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 16px 0;
}

.divider-rule[b-be0ltomtau] {
    flex: 1;
    height: 1px;
    background: var(--line);
}

.divider-label[b-be0ltomtau] {
    font-size: 12.5px;
    color: var(--mut2);
}

/* signin's caption under the demo button ("Read-only, seeded data..."). */
.demo-caption[b-be0ltomtau] {
    text-align: center;
    font-size: 12.5px;
    color: var(--mut2);
    margin-top: 6px;
}

/* sent's secondary line ("Nothing after a few minutes?..."). Left-aligned,
   unlike .demo-caption -- account.md:86 sets no text-align. */
.sent-secondary[b-be0ltomtau] {
    font-size: 12.5px;
    color: var(--mut2);
    margin-top: 12px;
}

/* account.md §2.1 -- inline text buttons: "Forgot?", the signin/create cross
   links, "Reset your password" (banner link), "← Back to sign in". Transparent,
   no border -- app.css's global "button { background: none; border: none; }"
   already gives both; only padding needs zeroing (the UA default isn't 0). */
.inline-link[b-be0ltomtau] {
    padding: 0;
    font-size: 13.5px;
    color: var(--acc);
}

.inline-link:hover[b-be0ltomtau] {
    text-decoration: underline;
}

/* The consent sentence belongs to the Create button above it, not to the
   "Already have one?" line below, so it sits tighter than a normal .foot-line. */
.consent-line[b-be0ltomtau] {
    margin-top: 12px;
}

/* The centered "New to Cardstock? ..." / "Already have one? ..." lines. */
.foot-line[b-be0ltomtau] {
    text-align: center;
    font-size: 13.5px;
    color: var(--mut);
    margin-top: 18px;
}

/* forgot's and reset's "← Back to sign in" line. */
.back-line[b-be0ltomtau] {
    text-align: center;
    margin-top: 18px;
}
/* /Pages/BinderPage.razor.rz.scp.css */
/* The page box (max-width 1480px, 20px gutters, 14/28 vertical) is <main>'s, drawn once in
   MainLayout.razor.css — the same way .home-main and the Card page rely on it. Restating it
   here nested two boxes and doubled every gutter (D-195 UAT, 2026-08-27; PageBoxTests). */
.binder-main[b-zfvnn1vt31] { display: flex; flex-direction: column; gap: 14px; }
.binder-title-row[b-zfvnn1vt31] { display: flex; align-items: center; gap: 10px; }
.binder-title[b-zfvnn1vt31] { margin: 0; font-size: 22px; }
.private-badge[b-zfvnn1vt31] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 10.5px; letter-spacing: 0.08em; padding: 2px 6px; border: 1px solid var(--line); border-radius: 4px; color: var(--mut2); cursor: help; }
.spacer[b-zfvnn1vt31] { flex: 1; }
/* The two title-row buttons are app.css's .btn-primary / .btn-secondary (D-301 row 46); the Export
   link's confirmation flash is this page's own state on top of that look. */
.btn-secondary.is-flash[b-zfvnn1vt31] { background: var(--posBg); color: var(--pos); border-color: rgba(24, 158, 99, 0.35); }
.binder-controls[b-zfvnn1vt31] { display: flex; justify-content: space-between; align-items: center; }
.binder-tabs[b-zfvnn1vt31], .binder-view[b-zfvnn1vt31] { display: flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.binder-tabs button[b-zfvnn1vt31], .binder-view button[b-zfvnn1vt31] { padding: 5px 12px; font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 11.5px; background: var(--card); color: var(--mut); border: 0; cursor: pointer; }
.binder-tabs button[aria-pressed="true"][b-zfvnn1vt31], .binder-view button[aria-pressed="true"][b-zfvnn1vt31] { background: var(--acc); color: var(--card); }
.binder-empty[b-zfvnn1vt31] { border: 1px solid var(--line); border-radius: 10px; padding: 34px 20px; text-align: center; background: var(--card); }
.binder-empty h2[b-zfvnn1vt31] { margin: 0 0 6px; font-size: 17px; }
.binder-empty p[b-zfvnn1vt31] { margin: 0 0 14px; color: var(--mut2); font-size: 13px; }
/* The app-wide error look (app.css, D-213): --neg text and a 3px --neg rule down the left edge,
   no glyph. Written here rather than inherited because a scoped `.binder-error[b-…]` outranks the
   global rule, and because this one is a full card panel — it keeps its own border, radius and
   ground, and only the left edge thickens. Its 20px padding already clears the bar. */
.binder-error[b-zfvnn1vt31] { border: 1px solid var(--line); border-left: 3px solid var(--neg); border-radius: 10px; padding: 20px; background: var(--card); color: var(--neg); }
/* app.css's `button { color: inherit }` would otherwise paint Retry --neg, and a red button reads
   as destructive when all it does is re-fetch. */
.binder-error button[b-zfvnn1vt31] { color: var(--ink); }
.iron-rule[b-zfvnn1vt31] { padding: 8px 12px; border-left: 3px solid var(--acc); font-size: 12px; color: var(--mut); }
.iron-rule-k[b-zfvnn1vt31] { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; letter-spacing: 0.08em; color: var(--ink); }

/* /Pages/BrowsePage.razor.rz.scp.css */
.browse-head[b-u12kifesmn] { display: flex; align-items: center; gap: 14px; }
.browse-head h1[b-u12kifesmn] { font: 700 26px 'Inter Tight', sans-serif; margin: 0; }
.set-grid[b-u12kifesmn] { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 12px; margin-top: 12px; }
.fan-tile[b-u12kifesmn] { display: block; background: var(--card); border: 1px solid var(--line);
    border-radius: 10px; padding: 14px; text-decoration: none; color: inherit;
    transition: box-shadow 0.15s; }
.fan-tile:hover[b-u12kifesmn] { box-shadow: 0 6px 20px rgba(20, 19, 26, 0.10); }
.fan[b-u12kifesmn] { position: relative; height: 118px; margin-bottom: 11px; }
.fan-card[b-u12kifesmn] { position: absolute; left: 50%; border-radius: 5px; overflow: hidden; }
.fan-back-left[b-u12kifesmn] { width: 74px; height: 102px; top: 4px;
    transform: translateX(-88%) rotate(-8deg); box-shadow: 0 3px 10px rgba(0,0,0,0.2); }
.fan-back-right[b-u12kifesmn] { width: 74px; height: 102px; top: 4px;
    transform: translateX(-12%) rotate(8deg); box-shadow: 0 3px 10px rgba(0,0,0,0.2); }
.fan-front[b-u12kifesmn] { width: 78px; height: 108px; top: 0; transform: translateX(-50%);
    box-shadow: 0 5px 14px rgba(0,0,0,0.25); }
.fan-card img[b-u12kifesmn] { width: 100%; height: 100%; object-fit: cover; }
.fan-name[b-u12kifesmn] { font: 600 15.5px 'Inter Tight', sans-serif; text-align: center; }
.fan-stats[b-u12kifesmn] { display: flex; justify-content: center; gap: 8px; font-size: 12px; margin-top: 4px; }
.fan-count[b-u12kifesmn] { color: var(--mut2); }
.fan-delta[b-u12kifesmn] { color: var(--mut2); }
.fan-pending[b-u12kifesmn] { font: 600 11.5px 'JetBrains Mono', monospace; color: var(--mut2);
    text-align: center; margin-top: 6px; }
.fan-value[b-u12kifesmn] { color: var(--ink); font-weight: 700; }
.fan-unpriced[b-u12kifesmn] { color: var(--mut2); font-weight: 400; cursor: help; }

.filter-bar[b-u12kifesmn] { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.filter-anchor[b-u12kifesmn] { position: relative; }
.add-filter[b-u12kifesmn] { border: 1px dashed var(--line3); border-radius: 6px; background: none;
    padding: 4px 10px; font-size: 12.5px; color: var(--mut); cursor: pointer; }
/* The pill that used to live here is deleted: D-228 ruling 2 made the prototypes' chip a
   component, and Browse renders <FilterChip /> now. A rule left behind would not even reach
   it — Blazor scopes a rule to the component that authors the markup. */
.species-caption[b-u12kifesmn] { font-size: 12.5px; color: var(--mut2); margin: 6px 0 0; }
.species-grid[b-u12kifesmn] { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 12px; margin-top: 10px; }
.species-tile[b-u12kifesmn] { display: block; background: var(--card); border: 1px solid var(--line);
    border-radius: 10px; padding: 13px; text-decoration: none; color: inherit;
    transition: box-shadow 0.15s; }
.species-tile:hover[b-u12kifesmn] { box-shadow: 0 6px 20px rgba(20, 19, 26, 0.10); }
.sp-head[b-u12kifesmn] { display: flex; align-items: center; gap: 10px; }
.sp-id[b-u12kifesmn] { flex: 1; min-width: 0; }
/* D-113 normalization: the slot stays 68×56 so tile geometry never varies; inside it the
   art window is the measured art box × its clean factor (SpriteScale.Factor), and the
   full canvas <img> is scaled by that factor and shifted so only the art shows. */
.sp-sprite-slot[b-u12kifesmn] { width: 68px; height: 56px; flex-shrink: 0; display: flex;
    align-items: center; justify-content: center; }
.sp-art-window[b-u12kifesmn] { overflow: hidden; }
.sp-art-window img[b-u12kifesmn] { display: block; image-rendering: pixelated; }
/* Fallback when a species is missing from sprite-art.json (or the asset fails to load):
   the pre-D-113 1×-canvas draw — 1:1 for the 898 native 68×56 sprites, a fractional
   squeeze for the 127 Gen-9-era 96×96 ones. */
.sp-sprite[b-u12kifesmn] { width: 68px; height: 56px; object-fit: contain; image-rendering: pixelated;
    flex-shrink: 0; }
.sp-name[b-u12kifesmn] { font: 600 15.5px 'Inter Tight', sans-serif; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
.sp-printings[b-u12kifesmn] { font-size: 11.5px; color: var(--mut2); }
.sp-footer[b-u12kifesmn] { display: flex; justify-content: space-between; align-items: baseline;
    margin-top: 10px; }
.sp-value[b-u12kifesmn] { font: 700 14.5px 'JetBrains Mono', monospace; }
.sp-unpriced[b-u12kifesmn] { color: var(--mut2); font-weight: 400; cursor: help; }
.sp-delta[b-u12kifesmn] { font-size: 12px; color: var(--mut2); }
.shown-count[b-u12kifesmn] { font-size: 12.5px; color: var(--mut2); }
.spacer[b-u12kifesmn] { flex: 1; }
.empty-panel[b-u12kifesmn] { background: var(--card); border: 1px solid var(--line);
    border-radius: 10px; padding: 40px; text-align: center; font-size: 14px;
    color: var(--mut2); margin-top: 10px; }
/* /Pages/CardPage.razor.rz.scp.css */
/* card.md §2.6: two equal columns, minmax(0,1fr) each, 16px gap. */
.census-pair[b-hn6h1uf1t1] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 16px;
}
/* /Pages/CharacterPage.razor.rz.scp.css */
.char-header[b-8zv28efgrq] { display: flex; align-items: center; gap: 20px; background: var(--card);
    border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
.char-avatar[b-8zv28efgrq] { position: relative; width: 64px; height: 64px; border-radius: 50%;
    overflow: hidden; flex-shrink: 0; }
.char-initial[b-8zv28efgrq] { position: absolute; inset: 0; display: grid; place-items: center;
    font: 700 26px 'Inter Tight', sans-serif; color: rgba(255, 255, 255, 0.92); }
.char-avatar img[b-8zv28efgrq] { position: relative; width: 100%; height: 100%; object-fit: contain;
    image-rendering: pixelated; }
.char-identity h1[b-8zv28efgrq] { font: 700 26px 'Inter Tight', sans-serif; margin: 0 0 6px;
    letter-spacing: -0.01em; }
.char-chips[b-8zv28efgrq] { display: flex; flex-wrap: wrap; gap: 6px; }
.dex-chip[b-8zv28efgrq] { font: 600 11.5px 'JetBrains Mono', monospace; background: var(--mutbg);
    border: 1px solid var(--line); border-radius: 4px; padding: 2px 7px;
    color: var(--mut); cursor: help; }
.stat-tiles[b-8zv28efgrq] { display: flex; gap: 22px; }
.stat-tile[b-8zv28efgrq] { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.stat-label[b-8zv28efgrq] { font: 600 11px 'Inter Tight', sans-serif; letter-spacing: 0.06em;
    color: var(--mut2); }
.stat-value[b-8zv28efgrq] { font: 700 19px 'JetBrains Mono', monospace; }
.deferred-value[b-8zv28efgrq] { color: var(--mut2); }
/* D-219 (b)'s absent-money dash, the same three declarations as .fan-unpriced and .sp-unpriced
   (BrowsePage.razor.css:25, :61) -- CSS isolation is per-file, so the rule is duplicated rather
   than shared, exactly as this file's SetPage chrome below is. NOT .deferred-value: that is the
   90D tile's state, where a worker has not run yet and the ◌ says so. This value is not pending
   anything -- nothing this species printed has ever sold at PSA 10 -- so it takes the weight and
   the help cursor but none of the gated vocabulary. */
.stat-unpriced[b-8zv28efgrq] { color: var(--mut2); font-weight: 400; cursor: help; }
.toolbar-sentence[b-8zv28efgrq] { font-size: 12.5px; color: var(--mut2); }

/* Shared-with-SetPage chrome, copied verbatim from SetPage.razor.css: Blazor CSS
   isolation is per-file (page-scoped), so these class names do not cross pages and must
   be duplicated rather than referenced. */
/* D-157 removed .accent-bar here too. Note what it was: a hard-coded #2B2D42→#7E6BA8 gradient
   identical to SetPage's, overridden inline per species — so the rule never showed its own
   colours on this page at all. */
/* See SetPage.razor.css -- the breadcrumb's rules now live in Components/Breadcrumb.razor.css. */
.set-toolbar[b-8zv28efgrq] { display: flex; align-items: center; gap: 14px; margin-top: 4px; }
.shown-count[b-8zv28efgrq] { font-size: 12.5px; color: var(--mut2); }
.empty-panel[b-8zv28efgrq] { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    padding: 40px; text-align: center; font-size: 14px; color: var(--mut2); }
.set-footer[b-8zv28efgrq] { font-size: 12.5px; color: var(--mut2); }
.spacer[b-8zv28efgrq] { flex: 1; }
.pos[b-8zv28efgrq] { color: var(--pos); }
.neg[b-8zv28efgrq] { color: var(--neg); }
/* .row-link moved to app.css (global, final whole-branch review Critical 2) -- CardCell's
   anchor carries its own scope id and this page-scoped copy never matched it. */
.tile-name[b-8zv28efgrq] { font: 600 13.5px 'Inter Tight', sans-serif; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
.tile-stats[b-8zv28efgrq] { display: flex; justify-content: space-between; margin-top: 4px; font-size: 12.5px; }
.tile-setline[b-8zv28efgrq] { font-size: 11.5px; color: var(--mut2); margin-top: 2px; }
/* /Pages/ScreenerPage.razor.rz.scp.css */
/* The page box — max-width 1480px, 20px gutters, 14/28 vertical — is <main>'s, drawn once in
   MainLayout.razor.css. Restating it here would nest two boxes and double every gutter
   (D-195 UAT). What this adds is the prototype's two-column frame (Cardstock
   Screener.dc.html:50–52) inside that box: `flex: 1 0 auto` claims the height <main> already
   took from #app (D-221); `min-height: 0` lets the flex children shrink below their content
   (the rail and the results column are paginated and bounded now — D-234 walk finding 4 — and
   the page itself is the one scroller). */
.screener[b-t370pdt066] {
  flex: 1 0 auto;
  min-height: 0;
  display: flex;
  align-items: stretch;
}

/* :77 — the main column. `min-width: 0` keeps a wide results table from stretching the flex
   row instead of scrolling inside it. */
.screener-main[b-t370pdt066] {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-left: 18px;
}

/* :79's own `padding: 12px 18px 0` is the prototype clearing the viewport edges; <main>
   already does that here, so only the gap between the rail and the content is kept (above).
   The PAGE is the scroller (D-221), not this box: it shipped with `overflow: auto` on the
   belief that it was the one scroller on the page (§2.6), and it never was -- the document
   itself measured 6 M px with the virtualized table in it, this box's height being just its
   content. An overflow box that never scrolls is still a sticky descendant's scrollport, so
   the table's header pinned to this box's own top edge and left with the page (measured
   headless, D-234 walk finding 3: top at -2,823 px at scrollY 3000). Without it the header
   sticks under the 48px nav like Browse's. */
.screener-results[b-t370pdt066] {
  flex: 1;
  min-height: 0;
}

/* No screens yet (D-228, §7 q10). The rail beside this still shows "+ New screen", so this
   states what a screen is rather than repeating the button. Mirrors .binder-empty's panel. */
.screener-empty[b-t370pdt066] {
  padding: 34px 20px;
  text-align: center;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.screener-empty h2[b-t370pdt066] {
  margin: 0 0 6px;
  font-size: 17px;
}

.screener-empty p[b-t370pdt066] {
  margin: 0;
  font-size: 13px;
  color: var(--mut2);
}

/* The app-wide load-failed look (app.css, D-213): --neg text and a 3px --neg rule down the
   left edge, no glyph. Written here for the same reason BinderPage writes its own — a scoped
   `.screener-error[b-…]` outranks the global rule, and this one is a full panel. */
.screener-error[b-t370pdt066] {
  padding: 20px;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--neg);
  border-radius: 10px;
  color: var(--neg);
}

/* app.css's `button { color: inherit }` would otherwise paint Retry --neg, and a red button
   reads as destructive when all it does is re-fetch. */
.screener-error button[b-t370pdt066] {
  color: var(--ink);
}

.screener-error p[b-t370pdt066] {
  margin: 0 0 10px;
}

/* A write that failed, said in one line rather than a panel — the page still has its screen
   and its rail; only the last edit did not land. */
.screener-error-line[b-t370pdt066] {
  margin: 0;
  font-size: 13px;
  color: var(--neg);
}

/* The results band (`Cardstock Screener.dc.html:184–193`): the mode segment and, beside it, the
   density toggle or the kind filter on the left; the save status on the right (D-284 slice 3 §2).
   The prototype's own `padding: 10px 18px 0` is the viewport clearance <main> already gives, so
   only the left gutter that lines this up with the filter row above it is kept. */
.screener-band[b-t370pdt066] {
  display: flex;
  align-items: center;
  gap: 6px;
}

.screener-band-spacer[b-t370pdt066] {
  flex: 1;
}

/* :186–187 — one segmented control, not two buttons: the border is the group's and the divider
   is the second button's left edge, which is why `overflow: hidden` is what rounds the ends. */
.screener-view[b-t370pdt066] {
  display: flex;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
}

.screener-view button[b-t370pdt066] {
  height: 27px;
  padding: 0 10px;
  border: 0;
  background: var(--card);
  color: var(--mut);
  font-family: ui-monospace, "JetBrains Mono", Menlo, monospace;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}

.screener-view button + button[b-t370pdt066] {
  border-left: 1px solid var(--line);
}

/* D-284 slice 3 §2: the mode segment is the density toggle's styling with a count pill. */
.screener-modes button[b-t370pdt066] { display: inline-flex; align-items: center; gap: 6px; }

/* The pill: --acc on --accBg at rest; on the lit segment (an --acc fill) it inverts to --acc on --card. ContrastTests
   pins --acc on both surfaces. */
.screener-pill[b-t370pdt066] { font-size: 11px; font-weight: 700; line-height: 16px; padding: 0 6px; border-radius: 9px; color: var(--acc); background: var(--accBg); }
.screener-view button.is-on .screener-pill[b-t370pdt066] { color: var(--acc); background: var(--card); }

/* The kind filter's ▲ and ▼. The segment's stack stays as shipped; only the glyph names the glyph face (D-276 UAT round
   1, finding 3 — ui-monospace first is SF Mono on macOS, beside the log's JetBrains Mono arrows). */
.screener-kind-glyph[b-t370pdt066] { font-family: 'JetBrains Mono', monospace; }

/* The prototype's `termBg`/`termFg` swap. --acc on --card is the pair ContrastTests already
   pins (D-213's SELL picker row); the ratio is symmetric, so putting the ink on the accent
   rather than the accent on the ink needs no new row. */
.screener-view button.is-on[b-t370pdt066] {
  background: var(--acc);
  color: var(--card);
}

/* :191's slot. There is no Save button (D-229): the slot reports the auto-save instead —
   "Saving…" while the snapshot writes, then the shared 1800 ms confirm flash, then nothing.
   Mono and muted like the match label it answers to; the flash is --pos on the page ground,
   a pair ContrastTests pins in all four modes (its `--pos` / `--bg` rows). */
/* D-290 §7: backtest mode's read-only line, the rework mockup's band-right span — 12.5px --mut2 with a lock before
   it. --mut2 on the page ground is ContrastTests' row. */
.screener-readonly[b-t370pdt066] {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--mut2);
  white-space: nowrap;
}

.screener-status[b-t370pdt066] {
  font-size: 12.5px;
  color: var(--mut);
  white-space: nowrap;
}

.screener-status.is-flash[b-t370pdt066] {
  color: var(--pos);
}
/* /Pages/SetPage.razor.rz.scp.css */
/* D-157 removed .accent-bar. It was the only 4px gradient stripe in the app, on two pages, and
   the build's version matched neither the app nor the prototype it came from. */
/* The breadcrumb's rules moved to Components/Breadcrumb.razor.css, which every page now shares.
   The `margin: 8px 0` that sat here was a build addition -- the prototype's div (Set.dc.html:58)
   carries only font-size and colour, and MainLayout already spaces page children by 16px. */
.set-header[b-7qauc0f6ar] { display: flex; align-items: center; gap: 24px; background: var(--card);
    border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
.set-title-row[b-7qauc0f6ar] { display: flex; align-items: baseline; gap: 10px; }
.set-title-row h1[b-7qauc0f6ar] { font: 700 26px 'Inter Tight', sans-serif; margin: 0; }
.set-code[b-7qauc0f6ar], .set-era[b-7qauc0f6ar] { font: 600 11.5px 'JetBrains Mono', monospace; background: var(--mutbg);
    border: 1px solid var(--line); border-radius: 4px; padding: 2px 7px;
    text-transform: uppercase; letter-spacing: 0.05em; color: var(--mut); }
.set-meta-pending[b-7qauc0f6ar] { font: 600 11.5px 'JetBrains Mono', monospace; color: var(--mut2);
    border: 1px dashed var(--line3); border-radius: 4px; padding: 2px 7px; cursor: help; }
.set-subline[b-7qauc0f6ar] { color: var(--mut); font-size: 13.5px; margin: 6px 0 0; }
.set-toolbar[b-7qauc0f6ar] { display: flex; align-items: center; gap: 14px; margin-top: 4px; }
.shown-count[b-7qauc0f6ar] { font-size: 12.5px; color: var(--mut2); }
.exclusion-banner[b-7qauc0f6ar] { background: rgba(176, 127, 26, 0.06);
    border: 1px solid rgba(176, 127, 26, 0.25); border-radius: 8px;
    padding: 10px 14px; font-size: 13px; color: var(--warn); }
.empty-panel[b-7qauc0f6ar] { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    padding: 40px; text-align: center; font-size: 14px; color: var(--mut2); }
.set-footer[b-7qauc0f6ar] { font-size: 12.5px; color: var(--mut2); }
.spacer[b-7qauc0f6ar] { flex: 1; }
.pos[b-7qauc0f6ar] { color: var(--pos); }
.neg[b-7qauc0f6ar] { color: var(--neg); }
.pop-warn[b-7qauc0f6ar] { color: var(--neg); }
/* .row-link moved to app.css (global, final whole-branch review Critical 2) -- CardCell's
   anchor carries its own scope id and this page-scoped copy never matched it. */
.tile-name[b-7qauc0f6ar] { font: 600 13.5px 'Inter Tight', sans-serif; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
.tile-stats[b-7qauc0f6ar] { display: flex; justify-content: space-between; margin-top: 4px; font-size: 12.5px; }
/* /Pages/SettingsPage.razor.rz.scp.css */
/* profile.md §2 item 2/3: 760px reading-width column (narrower than the 1480px data
   screens), centered, its own padding stacked on top of MainLayout's <main> padding --
   the same layering AboutDataPage.razor.css already uses for its own narrower column. */
.settings-container[b-ackosrex0c] {
    max-width: 760px;
    margin: 0 auto;
    padding: 28px 24px 72px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* app.css already sets h1 to Inter Tight 700, margin 0; only size and the bottom margin
   are specific to this screen (AccountPage.razor.css's own h1 override is the precedent). */
.settings-head h1[b-ackosrex0c] {
    font-size: 27px;
    margin: 0 0 4px;
}

.sub-line[b-ackosrex0c] {
    font-size: 13.5px;
    color: var(--mut2);
}

/* app.css's global .mono utility supplies the JetBrains Mono family; only the size
   (13px, not the inherited 13.5px) is specific to this line -- AccountPage.razor.css's
   own .mono override is the identical precedent. */
.mono[b-ackosrex0c] {
    font-size: 13px;
}

.flash-banner[b-ackosrex0c] {
    font-size: 13.5px;
    color: var(--pos);
    background: var(--posBg);
    border-radius: 6px;
    padding: 8px 10px;
}

/* Shared card shell for both children (ProfileCard, AppearanceCard, and eventually
   Account/Danger zone in Tasks 19/20): one definition here via ::deep rather than
   duplicating it in every card's own scoped stylesheet. ::deep drops the scope
   requirement on the right-hand selector for any descendant, however many component
   boundaries lie between -- AppChrome.razor.css's ".tabs ::deep .tab" is the one-level
   precedent; this is the same mechanism one level deeper. */
.settings-container[b-ackosrex0c]  .settings-card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 18px 20px;
}

.settings-container[b-ackosrex0c]  .settings-card h2 {
    font: 700 17.5px 'Inter Tight', sans-serif;
    margin: 0 0 14px;
}
