/* Company search screen — visual design only.
   Source of truth: the published "Team365 Search Mockup" artifact, transcribed verbatim into
   mockup-style-spec.md and copied from there into this file. Do not add a rule here that isn't
   backed by that spec; do not touch predicate/filter/default behaviour from this file (it is
   CSS, so it structurally cannot).

   Loaded AFTER the DevExpress "blazing-berry" theme stylesheet (see App.razor) so these rules
   can override it. Every rule below is qualified under ".search-shell" (this screen's own root,
   already emitted by CompanySearch.razor) so nothing here can affect any other page's DevExpress
   controls or DOM. The colour tokens these rules read (var(--…)) are declared in
   tokens.css, which App.razor links immediately before this file.

   RUNTIME DEPENDENCY: Inter and IBM Plex Mono are pulled from Google Fonts (see the <link> in
   App.razor, right above this stylesheet's own <link>). An air-gapped deployment needs both
   faces vendored locally and that <link> replaced with a local @font-face -- the fallback
   stacks below keep the screen legible in the meantime, they do not replace doing that. */

/* ---------------------------------------------------------------------------------------------
   1. Design tokens -- moved to tokens.css (item 2835, WSH-12). App.razor links tokens.css
   immediately before this file, so every var(--…) below resolves exactly as before.
--------------------------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------------------------
   2. Type -- section 2. font-variant-numeric applies to the whole screen, not only numeric
   columns, per spec.
--------------------------------------------------------------------------------------------- */
.search-shell,
.search-shell * {
  font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-variant-numeric: tabular-nums;
}
.search-shell {
  font-size: 13px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  color: var(--ink);
}
.search-shell .mono,
.search-shell td.num {
  font-family: "IBM Plex Mono", monospace;
}

/* ---------------------------------------------------------------------------------------------
   3. Layout -- section 3. .search-page plays the mockup's ".app" (page-level max-width and
   margin); .search-shell plays ".chrome" (the bordered, shadowed card everything sits in).

   NO max-width (product owner, 2026-09-09: "make it taking the full width of page"). The
   mockup's own 1600px cap bites only above roughly 1656px of viewport, so at the 1226px this
   screen is reviewed at it changes nothing, and on a wider monitor it leaves dead space. Without
   it the criteria grid and the result grid use the space; the 14px side padding
   stays, on its own merit -- flush-to-the-edge fields were not asked for and read badly, and
   the missing cap does not touch the ActionBar footer's own reason for living outside this
   element (CompanySearch.razor's own comment above the <form>): that reasoning is about the
   14px padding, not the max-width.

   The horizontal margin is `0`, NOT `auto`: no max-width is not enough on its own -- measured
   at a 2200px viewport with an `auto` margin and no cap, this element stays 1441px wide and
   centred. .shell-body is `display: flex; flex-direction: column` (shell.css), and an
   `auto` horizontal margin on a flex item absorbs the free space instead of stretching into
   it: the computed margin resolves to 351px a side. `align-self: stretch` does not override
   that -- auto margins win. shell.css's comment on .shell-body warns that its child still
   centres but does not fill; this rule is where that matters.
--------------------------------------------------------------------------------------------- */
.search-page {
  margin: 14px 0;
  padding: 0 14px;
  background: var(--bg);
}

/* Item 3601 (the product owner, 2026-10-29: "much better now"): the page fills the height above the action bar and the
   card grows into it, so a short criteria entry leaves no bare background between the card and the bar, as the approved
   mockup's `.ws` and `.shell` rules do (`flex: 1 0 auto`; docs/design/ui/search/2026-10-22-search-redesign-mockup.html
   at d6d686201). .search-page is a flex item of the
   <form>'s .actionbar-host column (shell.css), so `flex: 1 0 auto` grows it and never shrinks it below its content; the
   shell's grid then stretches its rows to the extra height. At the two desktop tiers the grid has one row, so the
   editor runs down to the card's bottom; the index keeps `align-self: start` and stays sticky, and the results pane
   keeps `align-self: start`, so a short result does not stretch with the row. */
.search-page {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}
.search-page > .search-shell {
  flex: 1 0 auto;
}
/* At 900px and below (SearchTiers.OnePaneMaxWidth) the one-pane tier and a failed tier stack the panes in one column,
   and a stretched grid shares the extra height between its rows: measured on aba at 820x2400, +316px to the accordion's
   row and +316px to the results' row, the divider between them floating mid-card. The rows keep their content height
   and the spare height sits under the last pane. On the not-yet-known first paint only Main criteria shows, and this
   only stops it stretching. */
@media (max-width: 900px) {
  .search-page > .search-shell {
    align-content: start;
  }
}
.search-page h1 {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
}

/* SD-3574 M6: the heading row -- the <h1> and the Search Type control (CompanySearch.razor's .search-heading). The 10px
   that sat under the <h1> alone moves to the row, so the gap above the shell is what it was; the row wraps rather than
   shedding the control on a narrow window (content parity, the way .shell-topbar wraps). The label takes the editor
   fields' quiet micro-label ink at the mockup's 11.5px, and .search-type-control fixes the combo at the mockup's 132px
   so it does not stretch to the row. */
.search-page .search-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  margin: 0 0 10px;
}
/* Item 3601 (the product owner's ruling, 2026-10-29): the heading text is not shown, but the <h1> is kept for screen
   readers -- visually hidden, never `display: none` or `visibility: hidden`, which would take it from assistive
   technology too. Search Type stays at the row's left. The browser tab's title is PushTabTitleAsync's, not this element's,
   and does not change. The rule is company-overview.css's .visually-hidden. `.search-page h1` above keeps the heading's
   type, so showing it again is this one rule's removal. */
.search-page .search-heading h1 {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.search-page .search-type-field {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.search-page .search-type-field > label {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ink2);
}
.search-page .search-type-control {
  width: 132px;
}

/* SD-3446 R29, SD-3574 M18: the workspace's two fixed widths and the index's selection ink. tokens.css has no index
   tokens and is not edited (company.css declares its own --ix-* at :root for the company rail); these three are scoped
   to .search-shell in the mockup's three-block shape -- bare, under the dark media query guarded as
   :root:not([data-theme="light"]), and under :root[data-theme="dark"] -- so an explicit light or dark choice always
   wins over the OS setting. The widths are layout sizes, not colours, so they are declared once. The two widths are the
   large desktop tier's (item 3597, R4: a 200px index and a 400px editor; they were 232px and 600px); the smaller tier
   and the first paint redeclare them below. */
.search-shell {
  --ix-w: 200px;
  --ed-w: 400px;
  --ix-sel: var(--pane);
  --ix-sel-ink: var(--accent);
  --ix-mark: var(--accent);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .search-shell {
    --ix-sel: var(--accent-bg);
    --ix-sel-ink: var(--ink);
  }
}
:root[data-theme="dark"] .search-shell {
  --ix-sel: var(--accent-bg);
  --ix-sel-ink: var(--ink);
}

/* The shell is the workspace: index | editor | results as one three-column grid (SD-3446 R18), the page scrolls and
   no pane scrolls inside it. NO `overflow` here, and none on .search-page or .search-workspace-row either: an
   overflow other than `visible` on any ancestor makes THAT ancestor the sticky index's scroll container, and the
   index would pin to a box that never scrolls instead of to the window (SD-3574 M18). The gradient paints the index column's --rail and its 1px divider behind the
   grid items, so the divider runs the full height of the shell whichever of the index and the editor is taller.
   Border, radius and shadow are the card the shell has always been. */
.search-shell {
  background: linear-gradient(to right, var(--rail) 0 calc(var(--ix-w) - 1px), var(--line) calc(var(--ix-w) - 1px) var(--ix-w), var(--pane) var(--ix-w));
  border: 1px solid var(--line);
  border-radius: 6px;
  display: grid;
  grid-template-columns: var(--ix-w) var(--ed-w) minmax(0, 1fr);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .05), 0 8px 24px rgba(0, 0, 0, .05);
}

/* SD-3574 P1, debate objection 7: the index and the editor share one row element (CompanySearch.razor's
   .search-workspace-row). At every tier it draws no box -- `display: contents` -- so the index and the editor are the
   shell's own grid items and the three-column grid above is exactly as drawn (item 3597 R5: the smaller desktop tier
   narrows the tracks and stacks nothing). No `overflow`, `contain` or `transform` on it at any width: any of them
   would make it the sticky index's scroll container. */
.search-shell .search-workspace-row {
  display: contents;
}

/* The document scrolls and shell.css's sticky .actionbar covers its bottom edge, so a control the browser scrolls into
   view on focus (Tab, a result-row link) could land under the bar: it scrolls only far enough to bring the control
   inside the viewport, not clear of the footer. scroll-padding-bottom on the scrolling element is the bar's height, so
   the focus scroll stops above it (SD-3197 R2, the remedy ruled for bug 3185; docs/reviews/3515/browser-check.md,
   "Re-check on aba", Step 4: 1 of 29 focusable controls stayed covered at 1200x1083 before this rule). Scoped by
   :has(.search-shell), this screen's own root, so no other page that links this stylesheet or shares the document
   gets the padding. 49px is the bar on one line. Below 900px shell.css wraps the bar onto two rows (its "Narrow-width
   collision" block, the same breakpoint) and the bar is 71px, measured at 820px; widths below 820px are unmeasured, and
   the bar's height there may differ. The 900px block follows the base rule, so it wins by source order at equal
   specificity. */
html:has(.search-shell) {
  scroll-padding-bottom: 49px;
}
@media (max-width: 900px) {
  html:has(.search-shell) {
    scroll-padding-bottom: 71px;
  }
}

/* ---------------------------------------------------------------------------------------------
   The index -- Main criteria, the sections and the selected section's subsections
   (CriteriaIndex.razor). Mockup .ix rules, each prefixed with the component's root.
--------------------------------------------------------------------------------------------- */
/* SD-3446 R29, SD-3574 M18: sticky at the top of the window while the page scrolls, with NO scroller of its own -- no
   `overflow`, no `max-height`, no `height`. A long index makes the page taller, as an expanded rail always did, rather
   than starting a second scrollbar, and a section you open should not scroll inside a panel that is already below the
   fold (product owner's rejection of an internally-scrolling rail, 2026-09). `align-self: start` is what lets
   it pin: stretched to the row's height it would have nowhere to travel. The left radius is the card's own, inside the
   shell's 1px border. */
.search-shell .search-index {
  grid-column: 1;
  position: sticky;
  top: 0;
  z-index: 5;
  align-self: start;
  display: flex;
  flex-direction: column;
  background: var(--rail);
  border-right: 1px solid var(--line);
  border-radius: 5px 0 0 5px;
}
.search-shell .search-index .ix-list {
  padding: 2px 0 8px;
}
.search-shell .search-index .ix-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 32px;
  padding: 5px 10px 5px 14px;
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
  position: relative;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
}
.search-shell .search-index .ix-item:hover {
  background: var(--accent-bg);
}
.search-shell .search-index .ix-item[aria-current="true"] {
  background: var(--ix-sel);
  color: var(--ix-sel-ink);
  font-weight: 600;
  box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line);
}
.search-shell .search-index .ix-item[aria-current="true"]::before {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  bottom: 5px;
  width: 3px;
  border-radius: 0 2px 2px 0;
  background: var(--ix-mark);
}
.search-shell .search-index .ix-item .nm {
  flex: 1;
  min-width: 0;
}
/* The "loading" pill on a section whose lookups are still arriving (SD-3574 P5): the index's own loading state, the
   editor draws no banner. Item 3597 (UI states, Loading): the one-pane accordion's header shows the same pill, so the
   rules carry both selectors. Its spinner has its own keyframes name so nothing else in the app's sheets can retarget it. */
.search-shell .search-index .ix-item .ix-load,
.search-shell .search-accordion .acc-head .ix-load {
  font-size: 10.5px;
  color: var(--warn);
  background: var(--warn-bg);
  border: 1px solid var(--warn);
  border-radius: 9px;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.search-shell .search-index .ix-item .ix-load::before,
.search-shell .search-accordion .acc-head .ix-load::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  animation: search-index-spin .9s linear infinite;
}
@keyframes search-index-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .search-shell .search-index .ix-load::before,
  .search-shell .search-accordion .acc-head .ix-load::before {
    animation: none;
  }
}
.search-shell .search-index .ix-item .tot {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10.5px;
  color: var(--ink3);
  min-width: 18px;
  text-align: right;
}
/* The selected section's subsections, indented under it behind a 1px guide. */
.search-shell .search-index .ix-sub {
  border-left: 1px solid var(--line);
  margin-left: 21px;
}
.search-shell .search-index .ix-sub .ix-item {
  padding-left: 20px;
  min-height: 28px;
  font-size: 12px;
  font-weight: 400;
  color: var(--ink2);
}
.search-shell .search-index .ix-sub .ix-item[aria-current="true"] {
  color: var(--ix-sel-ink);
  font-weight: 600;
}
.search-shell .search-index .ix-sub .ix-item[aria-current="true"]::before {
  left: 8px;
}
.search-shell .search-index .ix-group {
  padding: 12px 14px 4px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink3);
}

/* ---------------------------------------------------------------------------------------------
   The editor -- the one entry the index selected (SD-3446 R29). CriteriaSection.razor and MainCriteriaEntry.razor
   keep every entry they have shown in the tree and `hidden`, never removed (SD-3574 M4), so NO rule below gives
   .criteria-entry, .criteria-section or .criteria-main a `display`: a `display` declaration would beat the `hidden`
   attribute and show every entry at once (SearchCssLayoutTests.No_element_that_carries_hidden_is_given_a_display).
   .criteria-section-slot, the page's keyed wrapper around each section (SD-3574 M21), carries no `hidden` and draws
   nothing, so it has no rule either.
--------------------------------------------------------------------------------------------- */
/* No height and no overflow: the editor grows with the entry shown and the PAGE scrolls (SD-3574 M18). Item 3516 (spec
   AC5) re-scoped the control rules below to this class and retired `.search-rail` (SD-3574 M7). */
.search-shell .search-editor {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--pane);
  border-right: 1px solid var(--line);
}
/* The entry's head: the section as plain text above a subsection's name (SD-3574 P3), the name, and the Contact
   Information subsection's disambiguating subtitle (design deviation 5.1). */
.search-shell .criteria-entry-head {
  display: flex;
  flex-direction: column;
  padding: 12px 12px 10px 18px;
  border-bottom: 1px solid var(--line2);
}
.search-shell .criteria-entry-up {
  margin: 0;
  font-size: 11.5px;
  color: var(--ink3);
}
.search-shell .criteria-entry-head h2 {
  margin: 1px 0 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -.005em;
  color: var(--ink);
}
.search-shell .criteria-entry-sub {
  margin: 1px 0 0;
  font-size: 11.5px;
  color: var(--ink3);
}
.search-shell .criteria-entry-body {
  display: grid;
  gap: 8px;
  padding: 14px 18px 18px;
}
/* Item 3597 (R7, docs/reviews/3597/c9-small-editor.md, Step 4): the body's one grid track is sized to its widest
   unbreakable item, so a chosen survey's answer rows stretched it to 160-180px inside a 114px content box and every cell
   spilled with it. A grid item with `min-width: 0` no longer asks the track for its content's width, so the track stays at
   the editor's width and the row stacks (R7) or wraps (R8) inside it. One purpose, two scopes (final review N7): this rule
   holds at every tier; its smaller-tier twin just below, and the failed signal's copy in the L6 block, reach the grid
   items behind the display: contents wrapper. Merging them would change which boxes take min-width: 0, so they stay apart. */
.search-shell .criteria-entry-body > * {
  min-width: 0;
}
/* Round D (R7): .criterion-grain-body is `display: contents` (below), so the rule above reaches no box where the body's child
   is that wrapper -- the grid items are the wrapper's children, a survey's <details class="sec"> among them, and a form's
   summary (name plus count) still widened the track 0.9-1.4px past the smaller tier's 150px editor. */
.search-shell[data-tier="small"] .criteria-entry-body > .criterion-grain-body > * {
  min-width: 0;
}

/* ---------------------------------------------------------------------------------------------
   The criteria grids -- item 3516 (spec R1, R2, R6, AC1, AC3; 3597 R6, R13; SD-3446 R23). Every entry body draws its
   cells in .criteria-grid: two equal tracks, one when the body's content box is 340px or narrower. A row grid, not
   packed columns (3597 R13): cells keep catalogue order across each row, and a shorter cell leaves its row's space
   below it (EV7). Main criteria draws two grids (MainCriteriaEntry.razor); a subsection's survey questions follow its
   grid, in grids of their own (.sec .survey-question-grid). The container is the box whose content box the grid fills:
   every entry body, on every tier, and a survey form's padded .body for its one question grid. 363px at the large tier's editor gives
   two tracks, 113px at the smaller tier's one. The approved mockup's .cgrid, .cgrid + .cgrid and .cell.full / .cell.pair
   (docs/design/ui/search/2026-10-31-item3516-criteria-grid-mockup.html).

   Why the entry body and not the accordion's .acc-body (browser check F3, docs/reviews/3516/browser-check.md §3.3): the
   mockup puts its .cgrid straight into .acc-body, so there the container's content box is the grid's width. The app puts
   a .criteria-entry-body (18px padding each side) between them, and a nested group adds .acc-grp's border and
   .acc-gbody's padding, so querying .acc-body measured a loose section's grid 36px too wide and a nested group's 72px too
   wide: a 276px nested grid drew two tracks. Querying the nearest entry body keeps the mockup's threshold -- a grid of
   340px or narrower has one track -- at every depth. The survey form is the same case (browser check N1,
   docs/reviews/3516/browser-check.md §0.4): its <details class="sec"> was the box, but the .body inside it carries 26px of
   horizontal padding, so the question grid was 26px narrower than the box and kept two tracks at 315-340px. The padded
   .body is now the box and keeps its padding, and the grid is its one child (SurveyQuestionCriteria.razor), so a query
   measures the grid's own width -- accepted L2's 340px with no offset, and no compensating threshold.
--------------------------------------------------------------------------------------------- */
.search-shell .criteria-entry-body,
.search-shell .search-editor .sec .body {
  container-type: inline-size;
  container-name: criteria-editor;
}
.search-shell .criteria-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 14px;
  align-items: start;
}
.search-shell .criteria-grid > * {
  min-width: 0;
}
/* Main criteria's band break: the mockup's 16px less the body's own 8px gap. */
.search-shell .criteria-grid + .criteria-grid {
  margin-top: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--line2);
}
/* The cells spanning the row: the Special Company Filter (SD-3446 R23), a From/To range and a cell holding one (spec R6). */
.search-shell .criteria-grid > .criterion-wide {
  grid-column: 1 / -1;
}
/* A container rule adds no specificity, so it wins over its base rule only by coming after it: this one follows the base
   .criteria-grid rule above, and the survey's twin follows the base .survey-question-grid rule in section 4 (browser
   check F1). */
@container criteria-editor (max-width: 340px) {
  .search-shell .criteria-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------------------------------------------------------------------------------------------
   Main criteria -- the legacy's two always-visible bands above its first collapsible section, and the Special
   Company Filter (MainCriteriaEntry.razor), laid out as they were in the top bar (SD-3574 B2, M7). The six rules that
   styled .search-topbar are re-scoped to the editor with four classes in each selector, so they beat the three-class
   `.search-editor` control rules in section 4.

   Main criteria (MainCriteriaEntry.razor) draws two criteria grids, Band A with Source and then the rest of Band B with
   the Special Company Filter (item 3516, spec AC3, EV6; the grid rules are in *The criteria grids* below). The padding
   comes from .criteria-entry-body; the editor is the container, so the old bar's own --rail background and border are
   gone.

   The Search button is not a child here: it renders in the shell's footer action bar -- see CompanySearch.razor's own
   comment above <ActionBar> -- so this file sizes no wrap container for it. */

/* Each top-of-form criterion (Company Name, Active Filter, Prospect Filter, ...) plays the
   mockup's ".fld": a micro-label stacked above its control. */
.search-shell .search-editor .criteria-main .criterion,
.search-shell .resbar .search-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 0;
}
.search-shell .search-editor .criteria-main .criterion > label,
.search-shell .search-editor .criteria-main .criterion > .criterion-grain-body > label,
.search-shell .resbar .search-field > label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink3);
}

/* ---------------------------------------------------------------------------------------------
   4. Criteria controls -- the controls inside an editor entry. Section 4. Scoped to `.search-editor` (item 3516, spec AC5; SD-3574 M7).
--------------------------------------------------------------------------------------------- */
.search-shell .search-editor .criterion {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.search-shell .search-editor .criterion > label,
.search-shell .search-editor .criterion > .criterion-grain-body > label,
.search-shell .search-editor .criterion > .criterion-grain-body > .survey-instance-body > label,
.search-shell .search-editor .criterion > .criterion-grain-body > .criterion-label-row > label,
.search-shell .search-editor .criterion-date-pair-caption {
  font-size: 10.5px;
  color: var(--ink2);
  font-weight: 500;
}

/* SD-96: the ONE nested collapsible SurveyQuestionCriteria.razor
   adds, one per selected Survey Form, inside the "Survey" subsection's own .criteria-entry-body. A native
   <details>/<summary> (this component has no server round trip to gate a first-expand render behind, so there
   is nothing for a click handler to guard here) -- chrome and tokens as the mockup's own .sec/.f/.n
   (docs/design/ui/2026-09-09-mockup-style-spec.md §4), ported literally. */
.search-shell .search-editor .sec {
  border-bottom: 1px solid var(--line2);
}
.search-shell .search-editor .sec > summary {
  list-style: none;
  cursor: pointer;
  padding: 5px 12px 5px 14px;
  display: flex;
  gap: 6px;
  align-items: center;
  font-weight: 600;
  font-size: 11px;
  color: var(--ink2);
}
.search-shell .search-editor .sec > summary::-webkit-details-marker {
  display: none;
}
.search-shell .search-editor .sec > summary:hover {
  background: var(--accent-bg);
}
.search-shell .search-editor .sec .chev {
  color: var(--ink3);
  font-size: 8px;
  transition: transform .12s;
}
.search-shell .search-editor .sec[open] > summary .chev {
  transform: rotate(90deg);
}
.search-shell .search-editor .sec .n {
  margin-left: auto;
  font-family: "IBM Plex Mono", monospace;
  font-size: 10px;
  color: var(--ink3);
  background: var(--line2);
  padding: 1px 6px;
  border-radius: 9px;
}
.search-shell .search-editor .sec .body {
  padding: 6px 12px 10px 14px;
}
/* Item 3516 (spec AC1, accepted L2): each survey form's questions are a criteria grid too -- the mockup's .qbody.cgrid; one
   track when the grid itself is 340px or narrower. The grid is the padded .body's one child and the .body is the query box
   (*The criteria grids*, browser check N1), so the box's content box is the grid's width. The narrow rule below must
   follow this base grid rule: they carry the same selector and source order decides (browser check F1). */
.search-shell .search-editor .sec .survey-question-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  align-items: start;
}
@container criteria-editor (max-width: 340px) {
  .search-shell .search-editor .sec .survey-question-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
.search-shell .search-editor .sec .survey-question-grid > * {
  min-width: 0;
}
.search-shell .search-editor .sec .f {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.search-shell .search-editor .sec .f label {
  font-size: 10.5px;
  color: var(--ink2);
  font-weight: 500;
}
/* Item 3169, SD-3232 rulings 2 and 5: a Description (12) question is section text, not a control. TipText ''
   (legacy .surDescPromptText, which survey.css:1-8 makes bold italic) is bold italic in --ink, one step above the
   10.5px --ink2 prompts so it reads as a heading over the rows that follow. TipText 'BOLD' is the same weight upright:
   Team365's own look under SD-17, because legacy's computed look for .surDescPromptTextBold was not traced; its weight is
   restated on the rule so the rule text pins it (Search_css_gives_bold_descriptions_bold_upright_and_plain_descriptions_italic). No margin
   and no padding, so a grouped Desc child sits flat at its row position (ruling 5); the .survey-question-grid's 8px gap
   spaces it like any row. */
.search-shell .search-editor .sec .body .survey-desc {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  font-style: italic;
  color: var(--ink);
}
.search-shell .search-editor .sec .body .survey-desc-bold {
  font-weight: 700;
  font-style: normal;
}

/* The per-question Include/Exclude and Match:All/Any segmented toggles -- a plain two-button
   group, not a new control: DxButton/DxCheckBox are not used here because there is no
   two-state DevExpress editor that renders as a segmented pair, so this follows the same
   plain-<button>-group shape the rest of this stylesheet already uses for binary choices
   rendered as buttons rather than checkboxes. */
.search-shell .search-editor .toggle {
  display: inline-flex;
  gap: 4px;
}
.search-shell .search-editor .toggle button {
  font: inherit;
  font-size: 10.5px;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: none;
  color: var(--ink2);
  cursor: pointer;
}
.search-shell .search-editor .toggle button.active {
  background: var(--accent-bg);
  color: var(--ink);
  border-color: var(--line2);
}

/* Item 3516 (spec AC4; 3597 L1, L5; the approved mockup's .lbl-row, .push, .oprow and .opsel): at the large tier a
   criterion's Exclude sits on its label's row, pushed to the end, and a comparison's operator sits beside its value in wide cells and above it in narrow cells (spec L1). */
.search-shell .search-editor .criterion-label-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.search-shell .search-editor .criterion-exclude {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
}
.search-shell .search-editor .criterion-compare-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
/* Spec L1 (late-round Q1 B; browser check F2, docs/reviews/3516/browser-check.md §4; AC7): a wide cell keeps the approved
   mockup's row -- its .opsel, a 148px operator, and the value taking the rest. Below 302px (148px operator, the row's 6px
   gap, 148px value) the operator stacks above its value, each the cell's full width, and the cell keeps its one track; the
   first F2 correction, an even split of a narrow cell (84.5px each at 175px), is withdrawn. The mockup's `width: 148px`
   becomes a 148px flex basis that neither grows nor shrinks: same width in the row, and none held once the row stacks.
   min-width: 0 lets each wrapper go below its content's width; nothing is clipped.

   The query box is the .criterion cell. The row's parent, .criterion-grain-body, is `display: contents` and so has no box
   to measure -- a size query against it is never true -- and the cell is a stretched grid item whose content box the row
   fills (a .excl cell's 8px padding included). It is named criterion-cell, never criteria-editor, so the grid queries above
   still find their entry body or survey form .body; every query in this file is named, so none stops at the cell by accident. Only a
   cell holding a comparison row is a container (:has(), already this app's browser floor, SD-57), so no other cell takes on
   inline-size containment. The smaller tier and the failed signal keep their own stacking rules below. */
.search-shell .search-editor .criterion:has(.criterion-compare-row) {
  container-type: inline-size;
  container-name: criterion-cell;
}
.search-shell .search-editor .criterion-compare-op {
  flex: 0 0 148px;
  min-width: 0;
}
.search-shell .search-editor .criterion-compare-row > :last-child {
  flex: 1 1 148px;
  min-width: 0;
}
/* Must follow the two base rules just above: same selectors, so source order decides. */
@container criterion-cell (width < 302px) {
  .search-shell .search-editor .criterion-compare-row {
    flex-direction: column;
    align-items: stretch;
  }
  .search-shell .search-editor .criterion-compare-op,
  .search-shell .search-editor .criterion-compare-row > :last-child {
    flex: none;
  }
}

/* Item 3516 (spec EV2, R6; the approved mockup's .drange, .to and .dr-cap): a From/To range is one row at the large tier
   -- From, "to", To -- the captions hidden. */
.search-shell .search-editor .criterion-date-pair-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.search-shell .search-editor .criterion-date-pair-row > .criterion-date {
  flex: 1 1 0;
}
.search-shell .search-editor .criterion-date-pair-to {
  flex: none;
  font-size: 11px;
  color: var(--ink3);
}
.search-shell .search-editor .criterion-date-pair-cap {
  display: none;
}

/* The two non-Choice per-question answer shapes still on the shared row rule -- String
   (DxTextBox plus its own Contains/Prefix .toggle) and Date (the paired DxDateEdit idiom
   Member Status History already ships, see .criterion-date above). Both lay out as a row
   with the same 8px gap the rest of this fieldset's .f already uses, rather than inventing a
   new spacing value. */
.search-shell .search-editor .criterion-string-answer,
.search-shell .search-editor .criterion-date-range {
  display: flex;
  align-items: center;
  gap: 8px;
}
.search-shell .search-editor .criterion-date-range label {
  font-size: 10.5px;
  color: var(--ink2);
}

/* Item 3170, SD-3253 R4, R10: the Float answer -- two DxTextBox bounds with "to" between them, each bound stacking its
   optional message over its box (legacy's msgQuestion_<QID>-min/-max span precedes its input, Search.xsl:79,88). The row
   takes the Int range's 8px gap; the message takes --stop, as the long-text message does. Item 3193: the Int answer's two
   text bounds share these rules. Item 3171: the Date answer's two bounds (.criterion-date-bound: label, optional message, DxDateEdit) share these rules, so a Date message looks and
   stacks as the Float one does, and the row aligns to the boxes' bottom edge rather than the shared row rule's centre. */
.search-shell .search-editor .criterion-int-range,
.search-shell .search-editor .criterion-float-range,
.search-shell .search-editor .criterion-date-range {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}
.search-shell .search-editor .criterion-int-bound,
.search-shell .search-editor .criterion-float-bound,
.search-shell .search-editor .criterion-date-bound {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.search-shell .search-editor .criterion-int-to,
.search-shell .search-editor .criterion-float-to {
  font-size: 10.5px;
  color: var(--ink2);
}
.search-shell .search-editor .criterion-int-bound .validation-message,
.search-shell .search-editor .criterion-float-bound .validation-message,
.search-shell .search-editor .criterion-date-bound .validation-message {
  font-size: 10.5px;
  color: var(--stop);
}

/* Item 3155, SD-3167 R17/R18: one Other box line per grid Other choice, below the drop-down -- the String answer's row
   idiom and 8px gap, wrapping when the label is long. */
.search-shell .search-editor .criterion-other-answer {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}
.search-shell .search-editor .criterion-other-answer .other-before,
.search-shell .search-editor .criterion-other-answer .other-after {
  font-size: 10.5px;
  color: var(--ink2);
}
/* Item 3597 (R7): at the smaller tier's 150px editor a row of controls stacks in its cell -- From above To, an answer's
   two bounds one above the other -- so nothing overflows the editor (docs/reviews/3597/c9-small-editor.md). The large tier
   keeps the rows as they are. The measured containers are the first four survey answer rows and the .toggle button
   pair; the date and float rows are the same shape and stack with them. */
.search-shell[data-tier="small"] .search-editor .criterion-int-range,
.search-shell[data-tier="small"] .search-editor .criterion-string-answer,
.search-shell[data-tier="small"] .search-editor .criterion-date-range,
.search-shell[data-tier="small"] .search-editor .criterion-float-range,
.search-shell[data-tier="small"] .search-editor .criterion-other-answer,
.search-shell[data-tier="small"] .search-editor .toggle {
  flex-direction: column;
  align-items: stretch;
}
/* Item 3516 (3597 R7): Exclude below its label; the operator above its value. */
.search-shell[data-tier="small"] .search-editor .criterion-label-row,
.search-shell[data-tier="small"] .search-editor .criterion-compare-row {
  flex-direction: column;
  align-items: stretch;
}
.search-shell[data-tier="small"] .search-editor .criterion-exclude {
  margin-left: 0;
}
.search-shell[data-tier="small"] .search-editor .criterion-compare-op,
.search-shell[data-tier="small"] .search-editor .criterion-compare-row > :last-child {
  flex: none;
}
/* Item 3516 (3597 R7): From above To, each under its caption; the "to" between them is dropped. */
.search-shell[data-tier="small"] .search-editor .criterion-date-pair-row {
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
}
.search-shell[data-tier="small"] .search-editor .criterion-date-pair-to {
  display: none;
}
.search-shell[data-tier="small"] .search-editor .criterion-date-pair-cap {
  display: block;
  font-size: 11px;
  color: var(--ink3);
}
/* Item 3597 (R8): at the smaller tier a label wraps as far as it needs -- no line cap -- and a long word breaks rather than
   running out of the editor; none is cut. Round D: a survey question's label is SurveyQuestionCriteria.razor's
   .criterion > .f > label, which the other selectors do not reach; unwrapped, "Attractions/Hotels/Restaurants" held the
   body track 36.5px past the 150px editor (docs/reviews/3597/browser-check.md, "Round D re-measure"). */
.search-shell[data-tier="small"] .search-editor .criterion > label,
.search-shell[data-tier="small"] .search-editor .criterion > .f > label,
.search-shell[data-tier="small"] .search-editor .criterion > .criterion-grain-body > label,
.search-shell[data-tier="small"] .search-editor .criterion > .criterion-grain-body > .survey-instance-body > label,
.search-shell[data-tier="small"] .search-editor .criteria-main .criterion > label,
.search-shell[data-tier="small"] .search-editor .criteria-main .criterion > .criterion-grain-body > label,
.search-shell[data-tier="small"] .search-editor .criterion > .criterion-grain-body > .criterion-label-row > label,
.search-shell[data-tier="small"] .search-editor .criterion-date-pair-caption {
  white-space: normal;
  overflow-wrap: anywhere;
}
/* Item 3597 spec L7 (final review S3): a survey form's <summary> (SurveyQuestionCriteria.razor) is a flex row -- chevron,
   label <span>, count pill .n -- and a flex item is no narrower than its longest word, so a long form name pushed the pill
   7.4px past the summary at 1000px, 3 of 3 (docs/reviews/3597/browser-check.md, "Round D re-measure"). The label shrinks
   and breaks anywhere; the chevron and the pill keep their width. */
.search-shell[data-tier="small"] .search-editor .sec > summary > span:not(.chev):not(.n) {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Item 3168, SD-3195: the fourth answer shape, Large Text -- the Contains/Prefix .toggle, the
   optional over-limit message, then a native <textarea>, in that order (the message sits between
   toggle and box as legacy's span does). It shares the row rule's 8px gap but stacks as a column,
   because a multi-line box beside the toggle would squeeze both in the rail's width.
   The <textarea> is the rail's only native form control, so nothing from the DevExpress theme
   paints it; without a rule it rendered as the browser's white default box on the dark panel.
   It takes the tokens of the one other native input in the app, shell.css's .login-input
   (--pane field, --ink text, --line border, 4px radius, a 2px --accent focus-visible outline),
   which all flip in both dark blocks of tokens.css. resize is vertical only so a drag cannot
   widen the box past the rail. The message takes --stop, the colour .login-error and
   .search-error already give error text; it is a full row of its own in the column, so the gap
   sets it apart from the toggle above and the box below. */
.search-shell .search-editor .criterion-long-text-answer {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.search-shell .search-editor .criterion-long-text-answer textarea {
  box-sizing: border-box;
  width: 100%;
  padding: 6px 8px;
  font: inherit;
  color: var(--ink);
  background: var(--pane);
  border: 1px solid var(--line);
  border-radius: 4px;
  resize: vertical;
}
.search-shell .search-editor .criterion-long-text-answer textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.search-shell .search-editor .criterion-long-text-answer textarea[aria-invalid="true"] {
  border-color: var(--stop);
}
.search-shell .search-editor .criterion-long-text-answer .validation-message {
  font-size: 10.5px;
  color: var(--stop);
}

/* Exclusion styling, for criteria that support it (section 4). The mockup's own markup has no
   live "exclude" checkbox to style around -- this screen's does (CriteriaSection.razor already
   renders one) -- so only the left rule is reproduced; the label is left to say "Exclude"
   itself rather than doubling that word via a ::after (see styling-report.md). */
.search-shell .criterion.excl {
  border-left: 2px solid var(--stop);
  padding-left: 8px;
}
.search-shell .criterion.excl > .criterion-grain-body > .criterion-label-row > label:first-child {
  color: var(--stop);
}

/* Slice F (docs/search-control-map.md §3): the two DevExpress editors bound directly by
   CssClass with no control shell of their own -- DxComboBox (Choice) and DxDateEdit (Date).
   Every multi-select criterion ships its own outer wrapper (.facet-multiselect,
   .facet-treeview) that this file already sizes; these two editors have neither, so the
   editor's own root needs the same treatment directly, at its own selector.

   Unscoped to `.search-editor` or `.criteria-main`, like .criterion-date-hint immediately below:
   both Choice and Date criteria render in the top-of-form band (Active Filter, Source,
   ContactCreatedOnOrAfter, ...) AND in the sections' entries (MembershipFilterType, the four
   Join/Expiry/PaidThru/Renewal date-range pairs, CompanyInclusionDate), and the fix -- fill
   the .criterion flex column's own width instead of the editor's intrinsic one -- is
   identical in both places. Font size matches .facet-ddb's own bare-editor convention
   (section 4, SD-72), so a Choice/Date field reads at the same size as a converted
   multi-select sitting next to it in the same rail section. */
.search-shell .criterion-choice,
.search-shell .criterion-date {
  width: 100%;
  min-width: 0;
  font-size: 11.5px;
}

/* The date editors' visible format hint, rendered inside the criterion's own
   <label> (CriteriaSection.razor's RenderCriterionControl) rather than as a note under the
   control -- deliberately NOT a revival of .criterion-unavailable-note, which SD-49 removed
   (see the SD-49 comment below) and which was a block-level explanation of a disabled
   control. This is a micro-label's own trailing qualifier: same line, and it adds no vertical
   rhythm to a form whose density section 3 is entirely about.
   --ink3 is the existing micro-label ink declared in tokens.css, so the hint is one step quieter
   than an entry-body label (--ink2) but exactly as quiet as a Main criteria one -- and Main criteria is where the
   only date criterion in an ungated section, ContactCreatedOnOrAfter, actually renders. There it
   is separated by weight, case and letter-spacing (the label is 700/uppercase/.05em, this is
   400/none/0), not by ink. */
.search-shell .criterion-date-hint {
  margin-left: 4px;
  color: var(--ink3);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}

/* The help-icon tooltip (CriteriaSection.razor, rendered inside the criterion's own <label>
   immediately after the text above). Without a rule it is an unstyled, unspaced &#9432; glyph
   sitting flush against the label text with no visual cue that it is interactive. The <label>
   itself is not a flex container (neither the Main criteria nor the rail label rule above declares
   one), so margin-left is what actually separates this glyph from the label text -- the identical
   spacing mechanism .criterion-date-hint just above uses for the same reason, matched here rather
   than assumed. cursor: help signals the affordance on hover; --ink3 matches the quiet, non-label
   ink .criterion-date-hint also uses. */
.search-shell .criterion-help {
  margin-left: 4px;
  cursor: help;
  color: var(--ink3);
}

/* SD-3446 R9, M4; SD-3561 P7: a help text placed BelowControl -- CriteriaSection.razor's <p class="help-text">,
   inside the criterion's cell after its control; today only Contact Listing Exclusion By Status. The approved
   mockup's .help-text rule (2026-10-22-search-redesign-mockup.html) with tokens.css's --ink3, the same quiet
   ink .criterion-help uses, at the rail's 11px. No test pins this rule; its only evidence is AC5's browser
   check, docs/reviews/3512/browser-check.md. */
.search-shell .help-text {
  margin: 2px 0 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--ink3);
}

/* No .criterion-unavailable-note (SD-49): a criterion belonging to a gated group that is not
   visible is not rendered disabled with an inline explanation -- the whole group is
   absent from the rail and the form together (CompanySearch.razor's own
   BuildRailSections/IsGroupVisible), so there is no disabled control for such a rule to
   style. --ink3 itself is declared in tokens.css for the badges and micro-labels that use
   it elsewhere. */

/* SD-72: the shared multi-select facet (FacetMultiSelect.razor) -- a DxDropDownBox hosting
   FacetMultiSelectBody.razor's DxTreeView (SD-73 d4 retired the checkbox DxListBox it first
   hosted). Ours are the wrapper's flex column, the closed editor's width and type size, and the
   unavailable note's type; the height the drop-down body may reach before it scrolls is
   .facet-treeview's, in the block below. Everything inside the panel is DevExpress's own
   markup and is deliberately left alone -- the nine unstyled walls this replaces are what happens
   when a list's own layout is hand-rolled. */
.search-shell .search-editor .facet-multiselect {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.search-shell .search-editor .facet-multiselect .facet-ddb {
  width: 100%;
  min-width: 0;
  font-size: 11.5px;
}
.search-shell .search-editor .facet-multiselect-unavailable .facet-unavailable {
  font-size: 11px;
  font-style: italic;
  color: var(--ink3);
}

/* SD-3203 R3 (bug 3184): the pending control state -- the same quiet italic as "options unavailable" above, which stays
   the failed-fetch state. A section's own loading marker is the index's pill (.ix-load, SD-3574 P5). */
.search-shell .search-editor .facet-multiselect-pending .facet-pending {
  font-size: 11px;
  font-style: italic;
  color: var(--ink3);
}

/* SD-72 decision 5 / search-control-map.md Slice E: Member Type / Dues Category's two-level use
   (FacetMultiSelect.razor + FacetMultiSelectBody.razor) -- a DxDropDownBox hosting a
   recursive-check DxTreeView, the one multi-select control since SD-73 d4. This REPLACES SD-50
   decision 2's two-level DxCheckBox tree
   (.membership-tree/.membership-member-type/.membership-dues-category(-ies)/.membership-unavailable,
   none of which CriteriaSection.razor renders or this file styles) -- the outer
   wrapper and closed editor are the block above's
   .facet-multiselect/.facet-multiselect-unavailable/.facet-ddb -- since SD-73 d4 there is one
   control, flat or two-level, so both blocks style the same element tree -- and .facet-treeview
   is this block's only rule.

   .facet-multiselect-tree (FacetMultiSelect.razor's outer wrapper) is a deliberate marker
   class with no rule of its own -- product owner, 2026-09-15 (SD-74). It is not a gap: every
   element carrying it also carries .facet-multiselect on the same class attribute, which already
   supplies the layout above (display:flex; flex-direction:column; min-width:0). Kept as a stable
   hook for tests and any future styling that wants to target the tree variant specifically,
   without disturbing .facet-multiselect's own consumers. Do not add a rule here on the strength of
   a CSS audit alone -- see search-control-map.md Sec 3 (slices R and A) and the blazor skill's own
   2026-09-15 correction for the record this comment exists to keep the next audit from re-finding. */
.facet-treeview {
  /* Taller than a flat single-level facet needs: a two-level tree with the largest measured
     member type (twenty dues categories) needs more vertical room per
     member type than one flat row does, before the panel's own scrollbar takes over. */
  max-height: 260px;
  min-width: 260px;
}

/* SD-53's Member Status list has no rules of its own. The legacy renders chkMemStatus
   "horizontal", 5 (buildAssocCriteria.asp:229) as a five-column HTML table; under SD-17 the four
   options and their ORDER BY Ordering sequence are the information architecture and are kept,
   and the table markup that laid them out is surface. The criterion renders through the shared
   FacetMultiSelect shell (CriteriaSection.razor) and is styled by the facet-shell rules above
   (.facet-multiselect and its compounds), not by anything of its own.

   Recorded here because SD-53's option set and order still govern the criterion; only a bespoke
   layout for them is absent. */

/* SD-54's Member Status History transition list has no rules of its own either: the criterion
   renders through the shared FacetMultiSelect shell, so its CSS is the shared shell's, owned by
   one component alongside its markup. That is SD-72 decision 10 -- a list control ships its CSS
   in the same commit as its markup, because a razor and a stylesheet owned apart can ship a list
   in raw document flow with neither owner seeing the gap -- and it is the reason this file's own
   facet-shell block exists.

   The legacy's 3x3 grid (Call objCheckGroup.Render(rsData, "chkMemStatusHist", "vertical", 3),
   buildAssocCriteria.asp:266) renders here as one stacked column, because nine labels of the form
   "Current -> Terminated" do not fit three columns, and because one column keeps
   ORDER BY S.Description, T.Description reading straight down -- which is the direction the
   legacy's column-major fill order reads it in. The shared shell renders that single vertical
   list in that order. */

/* ---------------------------------------------------------------------------------------------
   5. Results. Section 5.
--------------------------------------------------------------------------------------------- */
/* The results bar -- heading, count line and the two paging selectors (CompanySearchResultsGrid.razor).
   .search-pane is the flex column below; the bar carries no explicit `order` so it stays first
   (its default 0 sorts ahead of the omitted-criteria note's explicit 1, the truncation notice's 2, the grid's 3 and
   the pager row's 4, and it is also first in DOM among order-0 siblings).

   No pager renders in this bar -- see CompanySearchResultsGrid.razor's own comment. The pager sits in the pane, in
   .search-pager-row below the grid (ResultsPager.razor), at the foot of the results pane by SD-3446 R22. So this
   bar's only children are the heading, the count line and the two selectors below. `flex-wrap: wrap` is judged separately, just below this rule. */
.search-shell .resbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px 14px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--rail);
}
.search-shell .resbar h2 {
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
}
.search-shell .resbar .count {
  font-size: 12px;
  color: var(--ink2);
}
.search-shell .resbar .resbar-spacer {
  flex: 1 1 auto;
}
/* Desktop tiers: the wrapper takes no box of its own (display: contents), so the two fields stay direct flex items of
   .resbar and its desktop wrapping is exactly as before the wrapper (follow-up browser check at 1281: a boxed wrapper
   dropped both fields to the left of line 2; Claude's call, keeping L8's "desktop tiers unchanged"). */
.search-shell .resbar .resbar-fields {
  display: contents;
}

/* No .resbar-pager rule (the pager's own left-border grouping): CompanySearchResultsGrid.razor renders
   nothing with that class, and a rule with no matching element left in this stylesheet is
   exactly the kind of stale record the closing record sweep (docs/rules/RULES.md §4) exists to
   catch.

   flex-wrap: wrap is KEPT: .resbar's widest children are the two "Max results"/"per page"
   DxComboBoxes (inside .resbar-fields, which takes no box on the desktop tiers), and this bar sits in the results pane beside the index and the editor at the two desktop tiers, so its
   content width is narrower than the viewport there; on one pane (900px and below) it takes the shell's full width.
   Removing the wrap would let the second selector's border overflow .resbar's own right edge instead of dropping to a
   new line on a narrow pane. The width at which it wraps in the three-pane layout is not measured here; the browser
   check, docs/reviews/3515/browser-check.md, is its only evidence. */

/* The results pane, the shell grid's third column (SD-3446 R18). `align-self: start` sizes it to its own content
   and pins it to the top of the row, so a short result does not stretch to the editor's height. The shell no longer
   clips (no `overflow`, M18), so the pane's own right-hand corners carry the card's radius: the visually first child
   (the results bar, or the error paragraph) and the visually last one (the pager row, or the grid when a single page
   draws no pager row) each take the corner it sits in. They are named by class, never by :first-child / :last-child:
   the pane reorders its children with `order`, so DOM position names the wrong element whenever the person-grain
   omitted note (order 1, rendered ahead of the bar) is present (SearchCssLayoutTests pins it). */
.search-shell .search-pane {
  grid-column: 3;
  align-self: start;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--pane);
  border-radius: 0 5px 5px 0;
}
.search-shell .search-pane > .resbar,
.search-shell .search-pane > .search-error {
  border-top-right-radius: 5px;
}
.search-shell .search-pane > .search-pager-row,
.search-shell .search-pane:not(:has(> .search-pager-row)) > .search-grid {
  border-bottom-right-radius: 5px;
}

/* The pager row (SD-3446 R22; SD-3574 M17): ResultsPager.razor renders it, inside its own guard, so a result that fits
   on one page draws no empty row. Last in the pane's flex column -- `order` puts it after the notices and the grid
   whatever their DOM order. 46px is the mockup's .rs-foot row. */
.search-shell .search-pager-row {
  order: 4;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  min-height: 46px;
  padding: 8px 14px;
  border-top: 1px solid var(--line);
  background: var(--pane);
}

/* SD-3574 M12: the pane before any search, after Reset and after a grain switch. */
.search-shell .search-blank {
  min-height: 320px;
  display: grid;
  place-content: center;
  text-align: center;
  padding: 24px;
  color: var(--ink2);
}
.search-shell .search-blank h2 {
  margin: 0 0 4px;
  font-size: 14px;
  color: var(--ink);
}
.search-shell .search-blank p {
  margin: 0;
  font-size: 12.5px;
  max-width: 34ch;
}

/* Item 3597 (R5): the smaller-desktop tier, 901px to 1280px. Three panes side by side at fixed widths -- the grid above with
   narrower tracks; nothing stacks and nothing floats (SD-3574 P1 and M13 as rewritten). It replaces the stacked band
   (results full width under the index and editor) and the 1160px tablet stacking. */
.search-shell[data-tier="small"] {
  --ix-w: 150px;
  --ed-w: 150px;
}
/* Item 3597 (R3, R9, R19): one pane, 900px and below. No index pane (the page renders none): the accordion, then the
   results -- or the results first after a Search with matches (R12; .results-first, CompanySearch.ResultsFirst). One
   column, so the index column's gradient goes. */
.search-shell[data-tier="one"] {
  grid-template-columns: minmax(0, 1fr);
  background: var(--pane);
}
.search-shell[data-tier="one"] .search-editor,
.search-shell[data-tier="one"] .search-pane {
  grid-column: 1;
}
.search-shell[data-tier="one"] .search-editor {
  border-right: none;
  border-bottom: 1px solid var(--line);
}
.search-shell[data-tier="one"].results-first .search-editor {
  border-bottom: none;
  border-top: 1px solid var(--line);
}
/* One pane stacks the criteria and the results in one card. The shell does not clip (M18), so the pane in each of its
   corners draws it, as the three-pane pane rules above do: criteria first, the editor takes the top corners and the
   results the bottom ones, the bar or the error giving up the top-right corner it takes beside the editor; results first
   (R12), the results take the top corners -- the bar or the error both of them -- and the editor the bottom ones, the
   pager row or the grid giving up the bottom-right one. Batched review S5: these replace the 1280px band's corner rules,
   which went with the band; SearchCssLayoutTests pins both orders. */
.search-shell[data-tier="one"]:not(.results-first) .search-editor {
  border-radius: 5px 5px 0 0;
}
.search-shell[data-tier="one"]:not(.results-first) .search-pane {
  border-radius: 0 0 5px 5px;
}
.search-shell[data-tier="one"]:not(.results-first) .search-pane > .resbar,
.search-shell[data-tier="one"]:not(.results-first) .search-pane > .search-error {
  border-top-right-radius: 0;
}
.search-shell[data-tier="one"]:not(.results-first) .search-pane > .search-pager-row,
.search-shell[data-tier="one"]:not(.results-first) .search-pane:not(:has(> .search-pager-row)) > .search-grid {
  border-bottom-left-radius: 5px;
}
.search-shell[data-tier="one"].results-first .search-pane {
  border-radius: 5px 5px 0 0;
}
.search-shell[data-tier="one"].results-first .search-editor {
  border-radius: 0 0 5px 5px;
}
.search-shell[data-tier="one"].results-first .search-pane > .resbar,
.search-shell[data-tier="one"].results-first .search-pane > .search-error {
  border-top-left-radius: 5px;
}
.search-shell[data-tier="one"].results-first .search-pane > .search-pager-row,
.search-shell[data-tier="one"].results-first .search-pane:not(:has(> .search-pager-row)) > .search-grid {
  border-bottom-right-radius: 0;
}
/* Late ruling L8 (demo) and its follow-up review: on one pane the results bar's spacer ends the heading line, so the fields
   wrapper (.resbar-fields, both paging selectors) begins a line of its own, and `margin-left: auto` puts it against the bar's
   right edge -- side by side on a tablet; on a phone the wrapper fills its line and each field sits right-aligned inside it,
   right edges matching. Without the spacer the flexible one shared line 2 with Max results at 390px and per page dropped to
   line 3 at a different x. The bar itself takes no `justify-content`: pushing every line right sent a wrapped heading line
   (an event in scope, large totals) flush right with its last count left behind. */
.search-shell[data-tier="one"] .resbar .resbar-spacer {
  flex-basis: 100%;
  height: 0;
}
.search-shell[data-tier="one"] .resbar .resbar-fields {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px 14px;
  margin-left: auto;
}
/* Round C, C2: the editor's own children paint square backgrounds into its corners, so they round too -- Main criteria
   (the accordion's first child, --pane) in the top ones when the criteria come first, and the last section (--rail) in
   the bottom ones when the results come first. */
.search-shell[data-tier="one"]:not(.results-first) .search-accordion > .criteria-main {
  border-radius: 5px 5px 0 0;
}
.search-shell[data-tier="one"].results-first .search-accordion > .criteria-section-slot:last-child > .acc-sec {
  border-radius: 0 0 5px 5px;
}
/* Item 3597 (R27, R30): the first paint, before the browser reports its width (no data-tier on the shell). The server
   renders the three-pane tree; only the top row and Main criteria paint. On a desktop the index and results keep their
   space, empty, so nothing shifts when they appear; on a phone Main criteria is full width and the results are held back.
   The two widths are SearchTiers.LargeMinWidth - 1 and SearchTiers.OnePaneMaxWidth, the signal's own (SearchTierSignal);
   a change moves both. The editor's hidden section shells paint nothing at any width. */
.search-shell:not([data-tier]) .search-index,
.search-shell:not([data-tier]) .search-pane {
  visibility: hidden;
}
@media (max-width: 1280px) {
  .search-shell:not([data-tier]) {
    --ix-w: 150px;
    --ed-w: 150px;
  }
}
@media (max-width: 900px) {
  .search-shell:not([data-tier]) {
    grid-template-columns: minmax(0, 1fr);
    background: var(--pane);
  }
  .search-shell:not([data-tier]) .search-index,
  .search-shell:not([data-tier]) .search-pane {
    display: none;
  }
  .search-shell:not([data-tier]) .search-editor {
    grid-column: 1;
    border-right: none;
  }
}

/* Item 3597 spec late ruling L6 (final review S1): the width signal failed (data-tier="failed", SearchTierSignal.
   FailedTierValue), so no tier will come. The page keeps the three-pane tree and nothing is hidden: the failure is a
   data-tier value, so the :not([data-tier]) first-paint rules above no longer match. The panes are laid out by width
   with the signal's own two queries instead: above 1280px the base 200px / 400px tracks; to 1280px the smaller tier's
   150px tracks and its control rules (R7, R8), without which a row of controls overruns the 150px editor -- those
   control rules also reach 900px and below, where they stack From/To inside the full-width editor; accepted, since
   nothing is hidden and nothing overflows (item 3515 never stacked them there); at 900px and
   below item 3515's tablet stacking -- index, editor and results in one column, in DOM order, the index not pinned (a
   pinned full-width index would cover the editor as the page scrolls), the dividers turned to the stacking axis. No
   accordion: the page renders none without a tier. The 900px block follows the 1280px one, so its grid wins. */
@media (max-width: 1280px) {
  .search-shell[data-tier="failed"] {
    --ix-w: 150px;
    --ed-w: 150px;
  }
  .search-shell[data-tier="failed"] .criteria-entry-body > .criterion-grain-body > * {
    min-width: 0;
  }
  .search-shell[data-tier="failed"] .search-editor .criterion-int-range,
  .search-shell[data-tier="failed"] .search-editor .criterion-string-answer,
  .search-shell[data-tier="failed"] .search-editor .criterion-date-range,
  .search-shell[data-tier="failed"] .search-editor .criterion-float-range,
  .search-shell[data-tier="failed"] .search-editor .criterion-other-answer,
  .search-shell[data-tier="failed"] .search-editor .toggle {
    flex-direction: column;
    align-items: stretch;
  }
  /* Item 3516 (3597 L6): the label row and the comparison row stack as at the smaller tier. */
  .search-shell[data-tier="failed"] .search-editor .criterion-label-row,
  .search-shell[data-tier="failed"] .search-editor .criterion-compare-row {
    flex-direction: column;
    align-items: stretch;
  }
  .search-shell[data-tier="failed"] .search-editor .criterion-exclude {
    margin-left: 0;
  }
  .search-shell[data-tier="failed"] .search-editor .criterion-compare-op,
  .search-shell[data-tier="failed"] .search-editor .criterion-compare-row > :last-child {
    flex: none;
  }
  /* Item 3516 (3597 L6): the range row stacks From above To as at the smaller tier. */
  .search-shell[data-tier="failed"] .search-editor .criterion-date-pair-row {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
  }
  .search-shell[data-tier="failed"] .search-editor .criterion-date-pair-to {
    display: none;
  }
  .search-shell[data-tier="failed"] .search-editor .criterion-date-pair-cap {
    display: block;
    font-size: 11px;
    color: var(--ink3);
  }
  .search-shell[data-tier="failed"] .search-editor .criterion > label,
  .search-shell[data-tier="failed"] .search-editor .criterion > .f > label,
  .search-shell[data-tier="failed"] .search-editor .criterion > .criterion-grain-body > label,
  .search-shell[data-tier="failed"] .search-editor .criterion > .criterion-grain-body > .survey-instance-body > label,
  .search-shell[data-tier="failed"] .search-editor .criteria-main .criterion > label,
  .search-shell[data-tier="failed"] .search-editor .criteria-main .criterion > .criterion-grain-body > label,
  .search-shell[data-tier="failed"] .search-editor .criterion > .criterion-grain-body > .criterion-label-row > label,
  .search-shell[data-tier="failed"] .search-editor .criterion-date-pair-caption {
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .search-shell[data-tier="failed"] .search-editor .sec > summary > span:not(.chev):not(.n) {
    min-width: 0;
    overflow-wrap: anywhere;
  }
}
@media (max-width: 900px) {
  .search-shell[data-tier="failed"] {
    grid-template-columns: minmax(0, 1fr);
    background: var(--pane);
  }
  .search-shell[data-tier="failed"] .search-index {
    grid-column: auto;
    position: static;
    border-right: none;
    border-bottom: 1px solid var(--line);
    border-radius: 5px 5px 0 0;
  }
  .search-shell[data-tier="failed"] .search-editor {
    grid-column: auto;
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .search-shell[data-tier="failed"] .search-pane {
    grid-column: auto;
    border-right: none;
    border-radius: 0 0 5px 5px;
  }
  .search-shell[data-tier="failed"] .search-pane > .resbar,
  .search-shell[data-tier="failed"] .search-pane > .search-error {
    border-top-right-radius: 0;
  }
  /* Late ruling L8 and its follow-up review, as the one-pane tier's results-bar rules above (the spacer ends the heading
     line, the fields wrapper sits at the right edge): this block is one pane too. */
  .search-shell[data-tier="failed"] .resbar .resbar-spacer {
    flex-basis: 100%;
    height: 0;
  }
  .search-shell[data-tier="failed"] .resbar .resbar-fields {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 10px 14px;
    margin-left: auto;
  }
  .search-shell[data-tier="failed"] .search-pane > .search-pager-row,
  .search-shell[data-tier="failed"] .search-pane:not(:has(> .search-pager-row)) > .search-grid {
    border-bottom-left-radius: 5px;
  }
}

/* Item 3597 (R9, R20): the one-pane accordion (CriteriaSection's Accordion layout). Headers are buttons inside headings;
   the bodies carry `hidden` when closed, so no rule here gives them a display. The mockup's .acc* rules, re-scoped under
   the accordion section. */
.search-shell .search-accordion .acc-sec {
  background: var(--rail);
  border-bottom: 1px solid var(--line2);
}
.search-shell .search-accordion .acc-h,
.search-shell .search-accordion .acc-gh {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}
/* Late ruling L4 (round C, C5): every header is at least the 44px touch minimum the mockup gives its one-pane controls
   (.actionbar .dx-btn, mockup line 343); the accordion renders on one pane only, so no width scope is needed. Padding
   alone came to 43px under a coarse pointer. Border-box, so the floor is the header's whole height, padding included. */
.search-shell .search-accordion .acc-head,
.search-shell .search-accordion .acc-ghead {
  display: flex;
  gap: 7px;
  align-items: center;
  width: 100%;
  min-height: 44px;
  box-sizing: border-box;
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
}
.search-shell .search-accordion .acc-head {
  padding: 7px 12px;
  font-weight: 600;
  font-size: 12.5px;
}
.search-shell .search-accordion .acc-ghead {
  padding: 6px 10px;
  font-weight: 600;
  font-size: 12px;
}
.search-shell .search-accordion .acc-head:hover,
.search-shell .search-accordion .acc-ghead:hover {
  background: var(--accent-bg);
}
.search-shell .search-accordion .acc-head[aria-expanded="true"] {
  background: var(--pane);
}
.search-shell .search-accordion .acc-head .chev,
.search-shell .search-accordion .acc-ghead .chev {
  transition: transform .12s;
}
.search-shell .search-accordion .acc-head[aria-expanded="true"] .chev,
.search-shell .search-accordion .acc-ghead[aria-expanded="true"] .chev {
  transform: rotate(90deg);
}
.search-shell .search-accordion .acc-head .nm,
.search-shell .search-accordion .acc-ghead .nm {
  flex: 1;
  min-width: 0;
}
.search-shell .search-accordion .acc-head .n,
.search-shell .search-accordion .acc-ghead .n {
  font-family: "IBM Plex Mono", monospace;
  font-size: 10.5px;
  color: var(--ink3);
  background: var(--line2);
  padding: 1px 6px;
  border-radius: 9px;
  font-weight: 500;
}
.search-shell .search-accordion .acc-body {
  background: var(--pane);
  padding: 6px 14px 16px;
}
.search-shell .search-accordion .acc-grp {
  border: 1px solid var(--line2);
  border-radius: 4px;
  margin-top: 10px;
  background: var(--pane);
}
.search-shell .search-accordion .acc-gbody {
  padding: 4px 12px 14px 22px;
  border-top: 1px solid var(--line2);
}
.search-shell .search-accordion > .criteria-main {
  background: var(--pane);
  border-bottom: 1px solid var(--line2);
}
@media (prefers-reduced-motion: reduce) {
  .search-shell .search-accordion .acc-head .chev,
  .search-shell .search-accordion .acc-ghead .chev {
    transition: none;
  }
}
@media (pointer: coarse) {
  .search-shell .search-accordion .acc-head {
    padding: 14px 10px 14px 14px;
  }
  .search-shell .search-accordion .acc-ghead {
    padding: 12px 10px;
  }
}

.search-shell .search-error {
  padding: 10px 14px;
  color: var(--stop);
  background: var(--stop-bg);
  margin: 0;
}
.search-shell .search-truncated {
  background: var(--accent-bg);
  border-top: 1px solid var(--accent-line);
  border-bottom: 1px solid var(--accent-line);
  padding: 10px 14px;
  font-size: 12px;
  color: var(--ink2);
  margin: 0;
  /* .search-pane is the flex column above; CompanySearchResultsGrid.razor renders this notice BEFORE the
     grid (it has to -- the ceiling that produced it is only knowable once the fetch is in), but
     the mockup's own ".note" sits under the grid's scroll area. `order` reaches that layout
     with no markup change: CompanySearchResultsGrid.razor's structure, which CompanySearchResultsGridRenderTests
     pins, is untouched. */
  order: 2;
}

/* DxGrid (SD-14) -- real rendered markup verified against the running app against a real
   client core (docs/rules/RULES.md §7), not guessed: a custom element root (<dxbl-grid>), an inner
   <table class="dxbl-grid-table"> with a genuine <thead>/<tbody>, "dxbl-grid-header" cells and
   UNCLASSED <tr>/<td> data cells. CompanySearchResultsGrid.razor gives the "#", the stacked name/locale
   cell and Number a CellDisplayTemplate each, so all three are styled by the class each
   template renders (".row-num", ".company-name", ".company-name-locale", ".mem-num") -- not by
   nth-child, which would silently retarget the wrong cell the next time a column is added or
   reordered. Company Type and Company Category stay plain columns with no extra treatment.
   CssClass="search-grid" on <DxGrid> (CompanySearchResultsGrid.razor) is what qualifies every rule
   below. */
.search-shell .search-grid {
  order: 3;
  flex: 1 1 auto;
  min-height: 0;
  /* The grid is its own scroller (approach B, product owner 2026-09-29). Capped at the
     viewport minus the bars that stay on screen around it -- the results bar (.resbar) above, the pager row
     (.search-pager-row) under it and the sticky footer (shell.css .actionbar) below -- so that with the page scrolled
     to its end, results bar + grid + pager row + footer fill the window exactly and the grid's own scroll area,
     .dxbl-scroll-viewer-content, is what scrolls. That is what makes the header stick: see the
     header comment below. max-height, not height, so a short result set shrinks to its rows
     instead of leaving an empty box and a pointless scrollbar. Only .search-grid is capped; the
     nested child grid is .search-grid-nested, a different class, and does not match.
     304px is the measured maximum (SD-3574 M8; docs/reviews/3515/browser-check.md Step 6, 2026-10-29, page scrolled to
     its end, a 20-match two-page result with the truncation notice shown and no omitted-criteria notice): the sum of
     .resbar + truncation notice 39.4 + pager row 47 + the gap below the workspace about 14 + .actionbar 49 (71 at
     820px). .resbar is 69.2 on one line (1280px and below, 304 is not needed there: about 219), 127.4 at a 448-522px
     pane (1440 and 1366: 276.6) and 154.8 at a 363px pane (1281: 304.0, the widest sum measured); 820px measured 240.5.
     Those are item 3515's pane widths; item 3597's narrowest three-pane results pane is 515px (at 901px,
     docs/reviews/3597/browser-check.md Step 2, derived), above the 363px pane that set 304, so 304 still covers it.
     One figure for every width, so no band shows the results bar scrolled out of view; a window whose grid is shorter
     than the cap is unaffected. An omitted-criteria notice would add its own height (unobserved). */
  max-height: calc(100vh - 304px);
}
.search-shell .search-grid .dxbl-grid-table {
  width: 100%;
  border-collapse: collapse;
}
/* Sticky header (section 5's "th"). This file does not make the header sticky; the DevExpress
   theme does, with `.dxbl-grid>.dxbl-scroll-viewer>.dxbl-scroll-viewer-content>.dxbl-grid-table>thead
   { position: sticky; top: 0 }`. Sticky only pins against the nearest scroll container, which
   for that thead is .dxbl-scroll-viewer-content -- and that element scrolls only because of the
   max-height on .search-grid above. Uncapped, the grid grows to fit every row, the DOCUMENT
   scrolls instead, and the header scrolls away with the page.
   The theme also owns the stacking: the same thead selector carries `z-index: 2` (4 under its
   .dxSafari / .dxMacOSMobilePlatform variant), in blazing-berry and blazing-dark alike.
   This file adds no sticky rule of its own, because each would be dead or idle. On the thead,
   `position: sticky; top: 0; z-index: 2` would repeat the theme at a lower specificity, (0,2,1)
   against its (0,4,1). On th.dxbl-grid-header, `position: sticky; top: 0` would lose to the theme's
   `.dxbl-grid .dxbl-grid-table>thead .dxbl-grid-header-row>th:not(.dxbl-grid-empty-cell)
   { position: relative }` (measured: computed position stayed `relative`), and a `z-index: 2` would
   take effect on that relative cell but order nothing: the cell's only positioned child is the
   theme's .dxbl-grid-column-resize-anchor, and neither grid sets AllowColumnResize.
   No test asserts any of this -- no test in this repository reads a stylesheet -- so the header
   was verified in a browser on the company and person grids (1200x1075, TIA, 2026-09-29); that
   manual pass is its only evidence. Not checked: a person row with its child grid expanded (none
   on the pages sampled), a narrow window, and the dark theme. */
.search-shell .search-grid th.dxbl-grid-header {
  /* blazing-berry sets both of these on the header cell at a specificity/order this rule
     otherwise loses to (verified against the running app: without !important the cell kept
     the theme's fixed light-grey header background and dark text underneath it, the same
     class of override the tbody-td and tbody-tr rules below need, just harder to beat here).
     Two !importants, both against the same third-party rule -- not a wall of them. */
  background: var(--rail) !important;
  color: var(--ink2) !important;
  text-align: left;
  padding: 7px 10px;
  border-bottom: 1px solid var(--line);
  font-size: 10.5px;
  letter-spacing: .05em;
  text-transform: uppercase;
  font-weight: 700;
  white-space: nowrap;
}
.search-shell .search-grid tbody td {
  /* blazing-berry sets its own fixed (light-theme) text colour directly on grid cells, which
     .search-shell's inherited `color: var(--ink)` cannot win against on specificity alone --
     verified against the real rendered app in dark mode: row text was unreadable against the
     dark zebra stripe below (--row) until this rule named `color` explicitly. */
  color: var(--ink);
  padding: 6px 10px;
  border-bottom: 1px solid var(--line2);
  vertical-align: top;
}
/* Every row needs an EXPLICIT background, not only the even ones: blazing-berry's own default
   row background is a fixed light colour, and without this rule it wins on the odd rows (the
   ones the zebra rule below does not touch), leaving --ink's light dark-mode text unreadable
   on them -- the mirror image of the td text-colour bug above, found the same way. */
.search-shell .search-grid tbody tr {
  background: var(--pane);
}
/* Zebra stripe (SD-125). Counts data rows only: a plain nth-child(even) also counts the PerReg
   master grid's detail rows (one per family, carrying the nested family grid), so every master
   after a family lands on an odd position and the stripe falls on the detail rows instead
   (measured live with it: 2 of 36 masters striped, 33 of 34 detail rows). DxGrid stamps
   data-visible-index on data rows only -- detail rows and the empty row carry none, and neither
   kind has a row class to key on -- so "of [data-visible-index]" restores the parity. Grids
   without detail rows (the company grid) should select the same rows a plain nth-child(even) does
   -- inferred from DxGrid's DOM shape, measured on the person grid only, not on a company page.
   The :where() is load-bearing: the "of S" argument adds S's own specificity, so a bare
   :nth-child(even of [data-visible-index]) scores (0,4,2) and would beat both the nested-row rule
   below and the tr:hover rule (0,3,2) -- verified live, it left 41 nested rows striped. Wrapped,
   the rule scores (0,2,2) like the plain row rule above and wins on source order alone. */
.search-shell .search-grid tbody tr:where(:nth-child(even of [data-visible-index])) {
  background: var(--row);
}
/* Nested family rows stay unstriped (product owner's call, 2026-09-25, SD-125): a
   family reads as one block under its master. Without this, the nested grid's rows -- which are
   also ".search-grid tbody tr" descendants -- would stripe by their own parity. */
.search-shell .search-grid .search-grid-nested tbody tr {
  background: var(--pane);
}
.search-shell .search-grid tbody tr:hover {
  background: var(--accent-bg);
}
/* "#" and "Number" -- the legacy's own narrow row-number column (result.html col width="35")
   and the member number beside it, both styled per the mockup's "td.num" (section 5): muted,
   tabular monospace figures. BOTH carry it, because mockup-style-spec.md section 2 reserves the
   mono face for "identifiers and counts" and a member number is an identifier -- the mockup's
   own td.num was the member number's rule before the "#" column existed. ".mem-num"
   (CompanySearchResultsGrid.razor) is the member number's class hook. The "#" column's own
   Width="48px" (CompanySearchResultsGrid.razor) carries its narrowness; this carries the type treatment
   for both. */
.search-shell .search-grid .row-num,
.search-shell .search-grid .mem-num {
  font-family: "IBM Plex Mono", monospace;
  color: var(--ink3);
  font-size: 11.5px;
}
/* Company Name -- bold, per the mockup's ".nm". */
.search-shell .search-grid .company-name {
  font-weight: 600;
}
/* The Locale line stacked under the name (the legacy's own <br>, result.html:640-641) -- muted,
   per the mockup's ".loc". Its own sibling <div> (CompanySearchResultsGrid.razor), not nested inside
   .company-name, so font-weight is stated explicitly here rather than relied on as a reset. */
.search-shell .search-grid .company-name-locale {
  display: block;
  font-weight: 400;
  color: var(--ink3);
  font-size: 11.5px;
}

/* SD-50 decision 1: the fifth column's Member Type shape, rendered only when
   AssociationManagementEnabled is true (CompanySearchResultsGrid.razor). No new token: all four statuses
   map onto tokens already declared in tokens.css (bare :root, its dark
   prefers-color-scheme block, and its data-theme="dark" block -- the three places the token
   contract requires, and all three already carry --ok/--warn/--stop/--cool). SD-13: colour is
   never the only carrier here -- every span below carries the status WORD too
   (MemberTypesCell's StatusLabel; RegistrationsCell for registrations), and these rules
   only add colour to text that already says what it means. */
.search-shell .search-grid .member-type-entry {
  line-height: 1.4;
}
.search-shell .search-grid .member-type-entry + .member-type-entry {
  margin-top: 2px;
}
.search-shell .search-grid .member-type-empty {
  display: inline-block;
}
.search-shell .search-grid .member-type-status--current {
  color: var(--ok);
}
.search-shell .search-grid .member-type-status--terminated {
  color: var(--stop);
}
.search-shell .search-grid .member-type-status--suspended {
  color: var(--warn);
}
.search-shell .search-grid .member-type-status--pending {
  color: var(--cool);
}
/* SD-50 decision 4: "inherited" is a word, not the legacy's '* ' prefix
   (searchCmp.asp:898-953, the prefix itself at :907) -- muted rather than coloured, because it
   is a qualifier on the membership beside it, not a second status. */
.search-shell .search-grid .member-type-inherited {
  color: var(--ink3);
  font-size: 11.5px;
  margin-left: 4px;
}

/* SD-60 decision 5: the Registrations column's own status palette, the same shape as
   .member-type-status just above -- rendered inside the same DxGrid (CompanySearchResultsGrid.razor's
   Registrations DxGridDataColumn), so it is qualified through .search-grid the identical way,
   not `.search-shell .registration-status` alone. Colour is SUPPLEMENTARY here: the status word
   (CompanySearchResultsGrid renders RegistrationType and StatusDescription through
   RegistrationsCell beside the dot) always
   renders too, which is what makes the cancelled-red / transferred-green pair acceptable where
   SD-13 would otherwise forbid it. The class comes from the status id, never from the word --
   see StatusClass(RegistrationStatus)'s own comment in RegistrationsCell.razor.
   Registered/Pending/Cancelled/Transferred reuse --accent/--cool/--stop/--ok because SD-60
   decision 5 names those four hues explicitly -- the choice was the product owner's, not this
   file's, so it is honoured even though it means --stop also renders Terminated, --ok also
   renders Current, and --cool also renders Pending on .member-type-status, in the same grid row
   whenever association management and an event are both in scope (see the SD-60 comment in tokens.css
   for the full reasoning, including why the --cool/Pending overlap is agreement rather than a
   collision). Qualified and Waiting get new tokens, --reg-qualified and --reg-waiting, precisely
   because decision 5 left both hues open -- with a free choice to spend, it went to avoiding a
   second amber that would read as Member Type's own Suspended restated, not to avoiding reuse in
   general. */
/* CompanySearchResultsGrid.razor wraps each grouped registration in its own <div class="registration-entry">,
   the exact wrapper .member-type-entry exists for two columns over. These rules give it the same
   purpose .member-type-entry has -- a readable line height, and a small gap between stacked
   entries when a company holds more than one registration -- because a company with two-plus
   registrations for the event otherwise stacks them with no separation at all. */
.search-shell .search-grid .registration-entry {
  line-height: 1.4;
}
.search-shell .search-grid .registration-entry + .registration-entry {
  margin-top: 2px;
}
/* No margin-right here: every .registration-status is the sole occupant of its own
   .registration-entry div, which is block-level and stacks vertically (see the rule above), so
   there is never anything to its right on the same line for a margin to separate it from. If a
   layout ever puts two of these side by side, add the margin there, qualified for that shape. */
.search-shell .search-grid .registration-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.search-shell .search-grid .registration-status::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.search-shell .search-grid .registration-status--registered {
  color: var(--reg-registered);
}
.search-shell .search-grid .registration-status--pending {
  color: var(--reg-pending);
}
.search-shell .search-grid .registration-status--cancelled {
  color: var(--reg-cancelled);
}
.search-shell .search-grid .registration-status--transferred {
  color: var(--reg-transferred);
}
.search-shell .search-grid .registration-status--qualified {
  color: var(--reg-qualified);
}
.search-shell .search-grid .registration-status--waiting {
  color: var(--reg-waiting);
}
/* A status id the enum does not define: no name to key a colour off, and the word still renders
   (StatusClass(RegistrationStatus)'s own Enum.IsDefined guard, RegistrationsCell.razor). */
.search-shell .search-grid .registration-status--unknown {
  color: var(--ink2);
}
.search-shell .search-grid .registration-none {
  color: var(--ink2);
}

/* SD-116: PersonSearchResultsGrid.razor's own Person Name / Location cell -- the
   person-grain sibling of .company-name/.company-name-locale just above, kept as separate
   classes rather than reused ones because a person row is not a company row (SD-94 decision 5
   gives the two record types sibling types rather than one shared one).
   Same treatment: bold name, muted locale. */
.search-shell .search-grid .person-name {
  font-weight: 600;
}
.search-shell .search-grid .person-name-locale {
  display: block;
  font-weight: 400;
  color: var(--ink3);
  font-size: 11.5px;
}
/* SD-125: legacy's ":563" span is INLINE, 25px after the locale line it sits inside of
   (result.html:555-564, TEAM1 LiveServerFiles@9bf021a3) -- not a sibling block-level "Linked,"
   line of its own. margin-left, not a gap or
   padding, so it reads as an indent past the locale text rather than a fixed offset from the
   cell's own left edge. Colour is unchanged: a link-styled colour (--accent, this codebase's
   existing token for an actionable/highlighted element -- see shell.css) is decoration on top of
   text that already says what it means, never the only carrier of a fact (no SD-13 concern). */
.search-shell .search-grid .person-linked {
  display: inline;
  margin-left: 25px;
  color: var(--accent);
  font-size: 11.5px;
}
/* result.html:559, :562 (TEAM1 LiveServerFiles@9bf021a3): the linked child's own name and Locale
   lines each carry margin-left:15px -- applied to .person-name and .person-name-locale only when
   the row is a linked child (PersonSearchResultsGrid.razor). SD-125: top-level rows only -- a nested
   child renders in PersonSearchChildGrid instead, which never applies this class at all (Q14). */
.search-shell .search-grid .person-linked-child {
  margin-left: 15px;
}

/* SD-125: the PerReg family subgrid renders as the parent list's own rows, shifted
   in -- product owner instruction 2026-09-25, "it should look like it's just shifted rows."
   Three rules, each CDP-measured against a real family in the running app:
   - Flush: DxGrid's td.dxbl-grid-detail-cell ships 28px padding on every side, which left
     ~29.5px of empty space above and below each family. Zeroed, so the family's rows sit
     against the rows around them as any two adjacent DxGrid rows do.
   - No nested border: search-grid-nested's theme-default 1px border made the family read as a
     separate boxed panel. Dropped.
   - Tabbed in: the nested grid's left margin is the rendered width of the master grid's "#"
     column plus 15px (the .person-linked-child figure above; Q14 keeps that class itself off
     nested rows). The child grid has no "#" column (Q8), so a plain 15px from the table edge
     left the child's name 35.5px LEFT of the master's; with the "#" width added the child's
     name starts 14.5px right of the master's (x=483 against x=468.5 -- the two grids' first-cell
     paddings cancel, so the offset lands on the 15px figure, not 65px).
   Nothing here names a colour, so there is nothing for a theme to get wrong. */
.search-shell .search-grid {
  /* The "#" column's RENDERED width: CDP-measured at 50px live for its Width="48px"
     (PersonSearchResultsGrid.razor, which points back here). A measurement, not derived from 48px
     -- keep it in step by re-measuring if that column's Width changes. */
  --row-num-column-width: 50px;
}
.search-shell .search-grid td.dxbl-grid-detail-cell {
  padding: 0;
}
.search-shell .search-grid .search-grid-nested {
  margin-left: calc(var(--row-num-column-width) + 15px);
  border: none;
}

/* SD-125, scope change 2026-09-25: the product owner asked to hide the nested
   grid's own column header too, so a family reads as pure shifted rows of the parent list rather
   than a second grid with its own header sitting inside the gap the two rules above already
   closed. There is no DxGrid parameter for this: decompiled DevExpress.Blazor.Grid.v26.1.dll
   (26.1.4) shows "ShowColumnHeaders" exists only on the internal XLSX/PDF export options
   converter (GridExportOptionsConverterBase, sets IDataAwareExportOptions.ShowColumnHeaders from
   ExportColumnHeaders) -- it governs an EXPORTED file's header row, not the live grid, is not a
   member of DxGrid or its base class anywhere in the type's own inheritance chain, and setting it
   on <DxGrid> would not compile. CSS is the only mechanism DxGrid actually exposes for this.
   Hiding the <thead> outright (not just the header cells) also settles a sticky-header overlap
   risk: with no header row rendered, the theme's own sticky thead
   rule (see the sticky-header comment above), which matches every DxGrid's thead, has nothing
   left to stick. */
.search-shell .search-grid .search-grid-nested thead {
  display: none;
}

/* ---------------------------------------------------------------------------------------------
   Touch ergonomics (2026-09-11 tablet tier). See shell.css's block of the same name for why
   this keys on the pointer rather than on a width, and for what is deliberately left out.

   These are the criteria index's entries (CriteriaIndex.razor): the most-tapped controls on the screen at tablet
   width. Padding rather than min-height, because each is a flex row whose content is a single line of text: padding
   grows the hit box symmetrically around the label, where min-height would pin the label to the top of a taller box.

   With a fine pointer this block does not fire, and an entry is 32px high (28px for a subsection). With a coarse
   pointer only the vertical padding changes, 5px to 14px, for a 44px touch target; the horizontal padding is the base
   .ix-item rule's (10px right, 14px left), unchanged, so an entry's label does not shift sideways between pointers.
   A subsection entry keeps its own 20px left inset, because the more specific .ix-sub .ix-item rule above outranks
   this one. The heights were measured at 820x1127 with an earlier 14px 12px padding here: 46.1px for a top-level entry
   and 45.4px for a subsection entry (docs/reviews/3515/browser-check.md, Controller addendum (a)). The vertical
   padding is the same 14px; this padding itself has not been measured in a browser.
--------------------------------------------------------------------------------------------- */
@media (pointer: coarse) {
  .search-shell .search-index .ix-item {
    padding: 14px 10px 14px 14px;
  }
}

/* ---------------------------------------------------------------------------------------------
   The grid fits its text instead of cutting it (2026-09-12 tablet tier --
   docs/superpowers/plans/2026-09-11-tablet-tier.md; product owner, 2026-09-12, option C).

   WHAT THIS REPLACES, so nobody re-derives the abandoned path. The tablet-tier plan would have
   hidden two columns below 1160px and re-rendered their values in a DxGrid detail row, driven by
   a DxLayoutBreakpoint -- a Blazor Server circuit round trip on every rotation and window drag.
   It was gated on a measurement, the measurement was taken (docs/spikes/2026-09-11-tablet-
   measurements.md), and what it found was a TEXT-FITTING failure rather than a column-count one:
   with the Registrations column in (SD-60), the grid at the 820px floor clips the caption
   "Company Name / Location" by 41px and silently cuts a registration line needing 162px in a
   136px cell. Both failures also occur at 1226px -- the width this screen is reviewed at -- so
   they were never tablet-specific, and a tablet-width breakpoint could not have fixed them.
   These three rules fix both, at every width, for no circuit and no hidden column.

   NO MEDIA QUERY, deliberately. Each rule only has an effect when the content does not fit, so
   the width at which it applies is decided by the content rather than by a number this file
   would then have to keep in step with the two above.

   1. HEADER CAPTIONS WRAP. The header cell's nowrap, above, is what clips them: DevExpress renders
      each caption in its own span with overflow:hidden and text-overflow:ellipsis, so a caption
      too wide for its column loses its tail rather than taking a second line. .shell-topbar is
      the precedent for the shape of this answer -- it wraps rather than sheds, for the same
      reason (content parity). The header is `position: sticky; top: 0` and its height is auto, so
      a two-line header still sticks and still measures correctly.
   2. DATA CELLS BREAK LONG TOKENS. Cells are already white-space: normal, so ordinary text wraps
      and always did; what truncates is a single unbreakable token -- one company name in 41 rows,
      and one member-type entry -- against the td's own overflow: hidden. `anywhere` rather than
      `break-word` because it also lowers the cell's min-content width, which is what lets the
      table give the column a sensible share in the first place.
      BOTH HALVES OF THE SELECTOR ARE LOAD-BEARING, AND THE `tr` IN THE FIRST IS NOT PADDING.
      blazing-berry.bs5.min.css ships `.dxbl-grid .dxbl-grid-table>tbody>tr>td` with
      `overflow-wrap: normal; overflow: hidden; text-overflow: ellipsis` -- specificity (0,2,3).
      `.search-shell .search-grid tbody td` is (0,2,2) and LOSES to it, so the td keeps `normal`
      and every child inherits `normal` from the td; the first rule below carries `tr` to reach
      (0,2,3) and win on source order, since App.razor links this file after the theme. The
      second, at (0,2,4), is what reaches the cells whose content is a div -- .company-name,
      .member-type-entry, .registration-entry -- which is most of them but not all: Company Type
      and Company Category are plain DxGridDataColumns whose text is a bare node in the td, and
      the theme's own ellipsis is what they fall back on. No !important either way, which was the
      other candidate and is not how this file argues with the theme.
   3. A REGISTRATION LINE WRAPS. `.registration-status` is nowrap (see its own rule above, from
      SD-60) and that is what cuts the two registration cells at the floor: nowrap defeats
      rule 2, which cannot wrap what is forbidden to wrap. The dot stays with the first line --
      it is the ::before of an inline-flex box, so it leads the line it is on.
--------------------------------------------------------------------------------------------- */
.search-shell .search-grid th.dxbl-grid-header,
.search-shell .search-grid th.dxbl-grid-header span[role="presentation"] {
  white-space: normal;
  text-overflow: clip;
}
.search-shell .search-grid tbody tr td,
.search-shell .search-grid tbody tr td * {
  overflow-wrap: anywhere;
}
.search-shell .search-grid .registration-status {
  white-space: normal;
}

/* SD-117 decision 2: a control the Person grain does not honour stays on screen, in place,
   DISABLED -- never hidden, never silently dropped, never enabled-then-refused.
   CriteriaSection.razor keeps it in its own .criterion cell and wraps the controls in an inert
   .criterion-grain-body; display: contents keeps that wrapper out of layout (the three label
   rules above are extended to reach through it), so the cell sits exactly where it sits on the
   Company grain. The wrapper is rendered on BOTH grains (one render shape, so the grain toggle
   never remounts a control); only its inert attribute varies, so the dimming keys on [inert].
   The note is outside the inert wrapper so it stays readable to assistive technology, and
   carries the field's label in .criterion-grain-note-field, hidden visually but read aloud
   (inert takes the label itself out of the accessibility tree). Only existing tokens are used. */
.search-shell .criterion-grain-body {
  display: contents;
}
/* Interim SD-117 decision 2 styling -- in live testing opacity .55 alone reads as still-live,
   worst on the dark theme with a pre-filled value, hence a floor of .4 opacity plus a
   muted field background on the DevExpress editors themselves (their .dxbl-* elements paint their
   own background, so opacity on the wrapper alone leaves an editor looking enabled), and
   cursor: not-allowed on the cell -- the cell itself is not inert, only .criterion-grain-body is,
   so the cursor renders. --rail is reused rather than a new colour: it is already the muted panel
   token in both the light and both dark blocks (tokens.css).
   The background rule EXCLUDES anything with "checkbox" in its class: DxCheckBox (rendered inside
   this wrapper -- Exclude, Contains modifiers, flags, Composition) paints its checked state as an accent
   background-color on .dxbl-checkbox-check-element with a white tick, not as an input surface: a
   blanket [class*="dxbl-"] match would overwrite that accent with --rail and make a
   ticked-but-disabled checkbox read as unchecked in the light theme, which breaks decision 2's
   "entered value stays visible". Text/combo/date editors have no such state to protect.
   Remove this whole block -- and the criterion-grain-unavailable class, the inert
   criterion-grain-body wrapper's unavailable branch, the note, and OmittedCriteriaNotice --
   once the last criterion is ported to the Person grain (SD-117). */
.search-shell .criterion-grain-body[inert] > * {
  opacity: .4;
}
.search-shell .criterion-grain-body[inert] [class*="dxbl-"]:not([class*="checkbox"]) {
  background-color: var(--rail) !important;
}
.search-shell .criterion.criterion-grain-unavailable {
  cursor: not-allowed;
}
.search-shell .criterion-grain-note {
  font-size: 10.5px;
  font-style: italic;
  color: var(--ink3);
}
/* ... and item 3516's visually hidden labels -- an operator's, a range input's. */
.search-shell .criterion-grain-note-field,
.search-shell .criterion-sr-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* SD-117 decision 10, results side: the stored values a person search did not apply, named. */
.search-shell .search-omitted-note {
  margin: 0 0 8px;
  font-size: 11.5px;
  color: var(--warn);
  /* Under the results bar: the page renders this note before the results component, whose bar is the pane's first
     flex item at the default order 0, so by DOM order alone the note would sit above the bar. */
  order: 1;
}

/* Item 86, SD-2993 R7 (design note 2026-10-02-survey-form-bare-note-design-note.md §7): the Survey Form
   cell names each bare selected form. The ink of .search-omitted-note (--warn), the rail's 11px, the lead
   in weight 600; no icon, no background, no new token. */
.search-shell .survey-bare-note {
  margin: 4px 0 2px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--warn);
}

.search-shell .survey-bare-note b {
  font-weight: 600;
}
