/* FPL-001 seam fix, found by the owner's live check of item 33 (2026-09-24): the sheet's own `body` rule, for a page built on the new layer.
   Every sheet (05, 06, 07) opens with `body { margin: 0; font: 14px/20px Inter; color: var(--text); background: var(--page) }`. The Blazor app never applied it: `body` kept the
   browser's 8px margin (a white strip around the dark top bar and the page — measured 1264 of 1280 px wide), `app.css`'s legacy `html, body { font-family: 'Helvetica Neue' }`, the 16px root
   size and black text — so anything that did not carry its own token (the access line, the drawer footer's text, an input's line height, every Fluent component) rendered in Helvetica/Arial
   at 16px beside neighbours in Inter 12–14px.

   FPL-001/REQ-028 (item 64, the ROLLOUT): every legacy MudBlazor route is hosted inside the new shell
   (LegacyMudLayout is `@layout AppShell` and wraps its content in `.legacy-scope` — a DIRECT child of
   `.ui-shell-content`; the overlay island's own `.legacy-scope` exists on every page and is not that) and
   NOW ALWAYS takes the sheet's body too (margin 0: the bar at (0,0) full width; the shell's font outside
   `.legacy-scope`) — `:has(.ui-shell)` alone is correct once every legacy route gets the same frame, with
   no exception clause needed. Item 37's own pilot version of this rule excluded any page whose shell
   content held a `.legacy-scope` UNLESS that island carried a per-route marker attribute (see
   LEGACY-PILOT.md for its name, kept as history); the rollout deletes the marker along with the 9-route
   hand list it existed to widen (LEGACY-FRAME.md has the
   rule, the mechanism and the measured numbers; LEGACY-PILOT.md keeps the pilot's own history).
   Loaded before supplier-fluent.css (which stays last). Tokens only, no literals (DesignLayerLint). */
/* FPL-001/REQ-028 (item 64, post-fix diagnostic pass): `.ui-shell`'s own `min-height: 100vh`
   (AppShell.razor.css, REQ-004/phase-2c -- the deliberate "every route fills the viewport" shell
   decision, unrelated to and predating this item, kept as-is) is a raw viewport unit that does NOT
   shrink when an UNRELATED horizontal scrollbar (a route's own overly-wide table, pre-existing on
   both BEFORE and AFTER identically -- verified live on /group, hScrollbarPx:10 on both sides)
   already eats some of the viewport's height. The shell then demands its full 100vh (900px in the
   sweep's own 1280x900 viewport) against only 890px actually available, forcing a SELF-INFLICTED
   vertical scrollbar on short-content routes that BEFORE never showed one on (BEFORE has no
   height floor at all -- it simply fits its short content). `height: 100%` on html/body (a plain,
   well-established percentage-height chain) resolves against the ACTUAL rendered box, which
   already accounts for any existing scrollbar reservation -- unlike a raw vh unit, so it does not
   fight one. `.ui-shell`'s own `min-height` changes from `100vh` to `100%` in the same commit
   (AppShell.razor.css) to complete this chain. Overflow stays visible (not set here), so this
   cannot clip a page whose content is genuinely taller than the viewport -- proved live:
   /group (short content, self-inflicted scrollbar gone, scrollHeight now matches BEFORE's own
   890px exactly); /agency (real "many-row" data, genuinely taller than the viewport -- 1111px
   scrollHeight, still scrolls, nothing clipped); and /crm/settings/representatives with real
   injected row data across three states (at rest, scrolled to the bottom, EditDrawer open via
   ?edit=new) -- all three byte-identical HEAD vs the commit immediately before this one
   (1025e6809), confirming drawerScroll.js and the EditDrawer's own footer are unaffected. */
html:has(.ui-shell) {
    height: 100%;
}
body:has(.ui-shell) {
    height: 100%;
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    color: var(--text);
    background: var(--page);
}

/* FPL-001/REQ-032 FR-2 (item 69, a staging review defect): the height chain above stops one link too
   soon. `index.html`'s `<div id="app">` -- Blazor's own mount point, the DIRECT parent of <AppShell>'s
   own rendered root, `.ui-shell` -- carries NO height rule of its own. A percentage height (`.ui-shell`'s
   own `min-height: 100%`, AppShell.razor.css) only resolves to a real length against a containing block
   that itself has a DEFINITE height -- `#app`'s own `auto` (content-sized) height breaks that chain right
   here, so `.ui-shell`'s `min-height: 100%` computes as if it were `auto`/absent entirely, and the whole
   shell just shrinks to its own content (the bar plus an island only as tall as ITS content) instead of
   filling the window. Confirmed live on /crm/leads at 1280x900: `#app` and `.ui-shell` both measured
   276px tall (nowhere near the 900px window), `.ui-shell-content`'s own white background stopped at
   y 276, and the computed background at y 880 read back `--page`'s own rgb(247, 248, 250) -- the exact
   grey band, at the exact shade, the review reported. `#app { height: 100% }`, scoped to a page that
   actually renders the new shell (same `:has(.ui-shell)` guard the two rules above already use, so a
   legacy-only surface with no `.ui-shell` at all is untouched), completes the chain: `.ui-shell`'s own
   `min-height: 100%` now resolves against a real 100%-of-viewport value, and `.ui-shell-content`'s
   existing `flex: 1` (AppShell.razor.css) then stretches it the rest of the way on its own -- nothing
   else here needed changing. */
body:has(.ui-shell) #app {
    height: 100%;
}

/* FPL-001/REQ-028 (item 64, the ROLLOUT of item 37's pilot) -- the two frame rules, now keyed on the
   island's plain STRUCTURE (`.ui-shell-content > .legacy-scope`) rather than a per-route marker: every
   LegacyMudLayout island is a direct child of `.ui-shell-content`, and the overlay island (mounted outside
   `.ui-shell-content` entirely) never matches this selector either way, so no marker is needed to tell
   them apart. Measured against the pre-wave production render at 1280 x 900 (originally on the pilot's two
   pages, LEGACY-PILOT.md; the rollout's own sweep re-measures all 174 routes, LEGACY-FRAME.md):
   (c)/(d) the shell's frame padding comes off the island's parent, and its ground is the layer's white
   (--surface) -- the white BEFORE the wave showed everywhere; with the padding gone the island is the full
   width but only as tall as its content, so the ground must be painted by the parent. */
.ui-shell-content:has(> .legacy-scope) {
    padding: 0;
    background: var(--surface);
}

/* (b) app.css's `.content { padding-top: calc(var(--topbar-height) + 8px) }` cleared the old FIXED bar; the shell's bar is in flow, so only the breathing room stays: bar + this + the page's own gutter
   puts the first line the same distance below the bar as before. Child combinators from the island: `.content` is a global class name and no other element may be reached. */
.legacy-scope > .page > main > .content {
    padding-top: var(--space-8);
}

/* FPL-001/REQ-018 (item 49) FR-1(a): the two focus-ring groups no single component owns. CSS isolation scopes a
   component's own `.foo:focus-visible` rule to elements THAT component renders (Blazor's own `[b-xxxxxx]`
   attribute); a plain `<a>` or `<input class="ui-in">` typed directly into a gallery specimen — no component of
   its own — carries no such attribute, so no scoped rule reaches it (found live, item 49's negative baseline:
   row 21's description-list link, row 79's cross-tab link, row 56's FieldValidation specimen, row 27's Tooltip
   demo radio all drew the browser's default outline). These two rules are GLOBAL (unscoped) fallbacks, not a
   replacement for any component's own rule — an existing scoped rule (`.ui-crumbs-link:focus-visible`, `.ui-mi:
   focus-visible`, …) does not always beat these on raw specificity alone (a compiled scoped selector can be LESS
   specific than a global one that adds its own extra qualifiers), but it always wins in practice because Blazor's
   scope attribute makes it the only rule of the two that matches that element's own compiled selector at all;
   these two only ever reach an element no scoped rule's compiled selector names.

   Excludes ANY ancestor `.legacy-scope`, not only a direct child of `.ui-shell-content` — a whole legacy PAGE
   wraps its content in one `.legacy-scope` directly under `.ui-shell-content` (the body rule above's own case),
   but a LEGACY ISLAND (REQ-016, item 46) nests `.legacy-scope` arbitrarily deep inside an otherwise new-layer
   page's own content (a drawer body, a page column) — a direct-child check alone does not exclude that (whole-
   branch review finding, 2026-09-26): an `:not(.legacy-scope, .legacy-scope *)` on the TARGET element itself
   (not on `.ui-shell-content`) catches both shapes at once, checked against every candidate, not just the whole
   page. `ShellOverlayIsland`'s own ambient `.legacy-scope` (mounted at the App root, outside `.ui-shell-content`
   entirely) is not this component's concern either way.
   Tokens only, no literals (DesignLayerLint). */
.ui-shell-content a:focus-visible:not(.legacy-scope, .legacy-scope *) {
    outline: var(--focus-w) solid var(--accent);
    outline-offset: var(--focus-offset);
}

.ui-in:focus-visible:not(.legacy-scope, .legacy-scope *) {
    outline: var(--focus-w) solid var(--accent);
    outline-offset: var(--focus-offset);
}
