/* =====================================================================
   Praxis Filters — shared filter system

   ORIGIN: v1.5, originally extracted from the Responsive Search project. As of
   2026-08-12 this file is OWNED HERE — do not re-extract over it. It was de-aliased
   onto the canonical token layer, so an upstream re-extraction would reintroduce the
   parallel vocabulary and silently revert the field treatment below.

   The centered filter modal (Standard + Custom in one tree), the quick-filter
   strip and the active-filter chip bar. Pair with praxis-filters.js:

     <link rel="stylesheet" href="praxis-filters.css" />
     <script src="praxis-filters.js"></script>

   Reads the canonical tokens directly (--praxis-* foundation, --px-* materials). The
   old scoping-to-filter-roots trick existed because search-page.html declares the same
   --s/--r/--t names for its Calendar module; with the aliases gone that collision
   cannot happen.
   ===================================================================== */

/* FIELD TREATMENT (2026-08-12): the text inputs here were the last surface still on the
   pre-2026-08-12 look — 1px neutral-20 border on --px-surface-2. They now take the same
   borderless --px-field treatment as .rfield__control / .admin-field__control, with the
   same inset focus ring and an AA-passing placeholder (was neutral-50). See §5A of the
   design-system reference. */

/* The v1.5 adapter block that used to live here is gone (2026-08-12): it declared a
   parallel --s/--r/--t/--d/--color/--shadow/--ease vocabulary and mapped it onto the
   Praxis layer. Every reference now names the canonical token directly, so this sheet
   reads from the same foundation as the rest of the app and there is no second
   vocabulary to keep in sync. */

/* Footer button variants the v1.5 modal adds on top of the host's .btn set.
   Scoped to the modal so they can't leak into a host page's own button scale. */
.filter-drawer .btn--neutral { background: var(--praxis-color-neutral-15); color: var(--praxis-color-neutral-90); }
.filter-drawer .btn--neutral:hover { background: var(--praxis-color-neutral-20); }
.filter-drawer .btn--neutral:active { background: var(--praxis-color-neutral-30); }
.filter-drawer .btn--clear { background: transparent; color: var(--praxis-color-red-60); border: 1px solid var(--praxis-color-red-60); }
.filter-drawer .btn--clear:hover { background: var(--praxis-color-red-10); }
.filter-drawer .btn--clear:active { background: var(--praxis-color-red-10); }

/* =====================================================================
   PRAXIS — footer buttons

   Clear and Reset take the same construction as the app's other buttons: a
   two-stop 180deg gradient, a hairline, a tight shadow, a coloured ambient
   glow and an inset top highlight — the shape of --px-primary-grad /
   --px-primary-shadow, in red for the destructive action and in neutral for
   Reset. In dark both switch to the soft-chip form the primaries use there,
   rather than a bright fill on a dark panel.

   The red stops are darker than a straight red-60 would be, on purpose.
   White on red-60 (#e22d38) is 4.39:1 and on a lighter top stop 3.93:1 —
   under AA. #d9303b → #bb2029 gives 4.73:1 and 6.27:1, so the label holds up
   across the whole gradient. (Worth noting --px-primary-grad's own top stop
   is 3.86:1 with white; this deliberately doesn't copy that.)

   Dark can't reuse --praxis-color-red-60: this file remaps it to #ff8b93 for
   dark because that value is meant to carry text ON a dark surface. As a
   fill it's a light salmon — white on it is 1.9:1 — so in dark it becomes
   the label on a deep red chip instead: 6.5:1.
   ===================================================================== */
body[data-variant="praxis"] .filter-drawer .btn--clear {
  border: 0; color: var(--praxis-color-white, #fff);
  background: linear-gradient(180deg, #d9303b, #bb2029);
  box-shadow: 0 0 0 .5px rgba(16,36,58,.2), 0 1px 2px rgba(16,36,58,.15),
              0 8px 18px -8px rgba(200,36,48,.5), inset 0 .5px 0 rgba(255,255,255,.28);
}
body[data-variant="praxis"] .filter-drawer .btn--clear:hover {
  color: var(--praxis-color-white, #fff); filter: brightness(1.06);
}
body[data-variant="praxis"] .filter-drawer .btn--clear:active { filter: brightness(.97); }

body[data-variant="praxis"][data-theme="dark"] .filter-drawer .btn--clear {
  color: #ff8b93;
  background: linear-gradient(180deg, rgb(72,26,32) 0%, rgb(54,20,25) 100%);
  box-shadow: 0 0 0 .5px rgba(255,139,147,.30), 0 1px 2px rgba(0,0,0,.35),
              0 4px 10px -8px rgba(255,139,147,.40), inset 0 .5px 0 rgba(255,255,255,.08);
}
body[data-variant="praxis"][data-theme="dark"] .filter-drawer .btn--clear:hover {
  color: #ffa4ab; filter: brightness(1.10);
}

/* Reset to Defaults — same construction, neutral. It restores displayed
   fields rather than destroying anything, so it carries no colour. */
body[data-variant="praxis"] .filter-drawer .btn--neutral {
  border: 0; color: var(--praxis-color-text-primary);
  background: linear-gradient(180deg, #ffffff, #eef1f5);
  box-shadow: 0 0 0 .5px rgba(16,36,58,.14), 0 1px 2px rgba(16,36,58,.10),
              0 2px 5px -2px rgba(16,36,58,.14);
}
body[data-variant="praxis"] .filter-drawer .btn--neutral:hover {
  background: linear-gradient(180deg, #ffffff, #e7ebf1);
  box-shadow: 0 0 0 .5px rgba(16,36,58,.18), 0 2px 8px -2px rgba(16,36,58,.18);
}
body[data-variant="praxis"] .filter-drawer .btn--neutral:active { filter: brightness(.98); }

body[data-variant="praxis"][data-theme="dark"] .filter-drawer .btn--neutral {
  color: var(--praxis-color-text-primary);
  background: linear-gradient(180deg, var(--px-surface-2,#232c3b), rgb(28,36,49));
  box-shadow: 0 0 0 .5px rgba(255,255,255,.14), 0 1px 3px rgba(0,0,0,.5),
              inset 0 .5px 0 rgba(255,255,255,.10);
}
body[data-variant="praxis"][data-theme="dark"] .filter-drawer .btn--neutral:hover {
  color: var(--praxis-color-white);
  background-image: linear-gradient(0deg, var(--px-hover-btn), var(--px-hover-btn));
  background-color: var(--px-surface-2,#232c3b);
  box-shadow: 0 0 0 .5px rgba(255,255,255,.20), 0 2px 6px rgba(0,0,0,.5), inset 0 .5px 0 rgba(255,255,255,.12);
}

/* Apply matches the toolbar's Run. In light they were already identical — both
   take the shared primary in praxis-core.css. Dark had them apart: that sheet
   gives toolbar primaries the soft chip but scopes it to
   :is(.toolbar, .rp-toolbar, .ws-toolbar, .admin-toolbar), and this footer is
   none of those, so Apply kept the bold cyan CTA.

   Note this reverses a deliberate call recorded in praxis-core.css — that
   in-dialog CTAs sit on their own surface, where the bold treatment still reads
   right. Matching Run was the explicit ask; revert this block to get the bold
   CTA back in dialogs. */
body[data-variant="praxis"][data-theme="dark"] .filter-drawer .btn--primary {
  background: var(--px-primary-soft);
  color: var(--px-primary-soft-fg);
  box-shadow: var(--px-primary-soft-shadow-sm);
}
body[data-variant="praxis"][data-theme="dark"] .filter-drawer .btn--primary:hover {
  background: var(--px-primary-soft); filter: brightness(1.06);
}
.filter-drawer .btn:disabled { opacity: .45; cursor: not-allowed; pointer-events: none; }

/* ---------------------------------------------------------------------
   Ported rules
   --------------------------------------------------------------------- */

/* Zero-specificity resets: the host page has its own global .btn / .icon-btn /
   .chip and box model. :where() keeps these at specificity 0 so the ported rules
   below still win, while stopping host globals from leaking into the panel.

   .filter-controls belongs in these lists and was missing. It is one of the
   component roots — the token bridge above already scopes to it — but its
   buttons were getting no reset, so the Show/Hide Quick Filters toggle
   rendered as a raw UA <button>: buttonface grey fill and a 2px outset
   border. Against the flat page that reads as permanently hovered/pressed,
   and the real :hover (a teal wash) had nothing to contrast against.

   These stay HERE rather than being appended at the end of the file: the
   reset has to sit above the component rules, or `color: inherit` would
   clobber .filter-toggle's accent colour. */
.filter-drawer :where(button), .drawer-scrim :where(button), .select-menu :where(button),
.quick-filters :where(button), .filter-chips :where(button),
.filter-controls :where(button) { border: 0; background: transparent; font: inherit; color: inherit; cursor: pointer; }
.filter-drawer :where(*), .drawer-scrim :where(*), .select-menu :where(*),
.quick-filters :where(*), .filter-chips :where(*),
.filter-controls :where(*) { box-sizing: border-box; }

/* The modal's own button + icon-button scale, so the host's .btn (36px, calendar
   primitive) doesn't shrink the v1.5 footer. */
.filter-drawer .btn { display: inline-flex; align-items: center; gap: var(--praxis-space-8); padding: 0 var(--praxis-space-16); min-height: 40px; border-radius: var(--praxis-radius-md); font-size: var(--praxis-type-size-base); font-weight: 600; font-family: inherit; cursor: pointer; }
.filter-drawer .icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: var(--praxis-radius-sm); color: var(--praxis-color-text-primary); }
.filter-drawer .icon-btn:hover { background: var(--px-chip); }
.filter-controls, .quick-filters, .filter-chips { flex-shrink: 0; }
.filter-controls { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--praxis-space-12); padding-bottom: var(--praxis-space-4); }
.filter-controls > .btn[data-action="open-filter-drawer"] { justify-self: start; }
.filter-controls > .filter-toggle { justify-self: center; }
.filter-toggle { display: inline-flex; align-items: center; gap: var(--praxis-space-4); color: var(--praxis-color-teal-60); font-size: var(--praxis-type-size-sm); font-weight: 500; padding: var(--praxis-space-4) var(--praxis-space-8); border-radius: var(--praxis-radius-sm); }
.filter-toggle:hover { background: var(--praxis-color-teal-10); }
.filter-toggle__caret { transition: transform var(--praxis-motion-fast) var(--praxis-ease-default); }
.filter-controls[data-collapsed] .filter-toggle__caret { transform: rotate(180deg); }
.segmented { display: inline-flex; border: 1px solid var(--praxis-color-neutral-20); border-radius: var(--praxis-radius-md); overflow: hidden; background: var(--px-surface); }
.segmented__opt { padding: 0 var(--praxis-space-16); height: 36px; font-size: var(--praxis-type-size-sm); color: var(--praxis-color-text-secondary); border-right: 1px solid var(--praxis-color-neutral-20); font-weight: 500; transition: background var(--praxis-motion-fast) var(--praxis-ease-default); }
.segmented__opt:last-child { border-right: 0px; }
.segmented__opt:hover { background: var(--px-chip); color: var(--praxis-color-text-primary); }
.segmented__opt--active { background: var(--praxis-color-neutral-90); color: rgb(255, 255, 255); }
.segmented__opt--active:hover { background: var(--praxis-color-neutral-80); color: rgb(255, 255, 255); }
.quick-filters { overflow: hidden; transition: max-height var(--praxis-motion-slow) var(--praxis-ease-default), opacity var(--praxis-motion-normal) var(--praxis-ease-default); max-height: 500px; }
.quick-filters[data-collapsed] { max-height: 0px; opacity: 0; }
.quick-filters__scroller { display: grid; grid-template-columns: repeat(5, minmax(0px, 1fr)); gap: var(--praxis-space-12); }
.qfilter { background: var(--px-surface); border: 1px solid var(--praxis-color-border-default); border-radius: var(--praxis-radius-md); padding: var(--praxis-space-12); display: flex; flex-direction: column; gap: var(--praxis-space-8); min-width: 0px; }
.qfilter__head { display: flex; align-items: center; gap: var(--praxis-space-8); }
.qfilter__title { font-size: var(--praxis-type-size-base); font-weight: 600; color: var(--praxis-color-text-primary); flex: 1 1 0%; min-width: 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qfilter__controls { display: flex; flex-direction: column; gap: var(--praxis-space-8); }
.qfilter__list { display: flex; flex-direction: column; gap: 2px; }
.qfilter__row { display: flex; align-items: center; gap: var(--praxis-space-8); font-size: var(--praxis-type-size-xs); padding: var(--praxis-space-4); color: var(--praxis-color-text-primary); border-radius: var(--praxis-radius-sm); cursor: pointer; transition: background var(--praxis-motion-fast) var(--praxis-ease-default); }
.qfilter__row:hover { background: var(--praxis-color-neutral-05); }
.qfilter__check {width: 16px; height: 16px; flex-shrink: 0; border: 2px solid var(--praxis-color-neutral-30); border-radius: 4px; background: var(--px-surface-2); color: rgb(255, 255, 255); display: grid; place-items: center; transition: background var(--praxis-motion-fast) var(--praxis-ease-default), border-color var(--praxis-motion-fast) var(--praxis-ease-default); }
.qfilter__check .material-symbols-rounded { font-size: 12px; }
.qfilter__row-label { min-width: 0px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.qfilter__row--selected { color: var(--praxis-color-neutral-100); font-weight: 600; }
.qfilter__row--selected .qfilter__check { background: var(--praxis-color-teal-60); border-color: var(--praxis-color-teal-60); }
.qfilter__more { font-size: var(--praxis-type-size-xs); color: var(--praxis-color-teal-60); font-weight: 600; text-align: center; padding: var(--praxis-space-4) 0; margin-top: auto; }
.qfilter__more:hover { text-decoration: underline; }
.filter-chips { display: flex; flex-wrap: wrap; gap: var(--praxis-space-8); padding: var(--praxis-space-8) 0 var(--praxis-space-12); }
/* High-contrast filter chips. Light mode: light fill + dark text/stroke.
   Dark mode: inverse — dark fill + light text/stroke. */
/* Same fixed-height trap as the status pill. A chip carrying a multi-value
   filter ("Assigned To: is Alexandria Gilbert, Safety Review Team, …") does
   wrap to a second line once the row is narrower than about 900px — but
   height:40px held the outline at one line, so the second line sat outside it.
   min-height lets the outline follow the text. Measured at 834 and 700px:
   clipped before, 52px and intact after. */
.filter-chips .chip { display: inline-flex; align-items: center; gap: var(--praxis-space-8); padding: var(--praxis-space-4) var(--praxis-space-16); min-height: 40px; max-width: 100%; text-align: left; background: var(--px-surface); border: 1px solid var(--praxis-color-neutral-90); border-radius: var(--praxis-radius-full); font-size: var(--praxis-type-size-base); font-weight: 600; font-family: inherit; color: var(--praxis-color-neutral-90); transition: background var(--praxis-motion-fast) var(--praxis-ease-default); }
/* The X never shrinks or wraps away from the label it removes. */
.filter-chips .chip__close { flex-shrink: 0; }
.filter-chips .chip:hover { background: var(--praxis-color-neutral-05); }
body[data-theme="dark"] .filter-chips .chip { background: var(--praxis-color-neutral-90); border-color: rgba(255,255,255,.7); color: #fff; }
body[data-theme="dark"] .filter-chips .chip:hover { background: var(--praxis-color-neutral-80); }
.filter-chips .chip strong { color: inherit; font-weight: 600; margin-left: 2px; }
.filter-chips .chip__close { width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; color: inherit; font-size: 24px !important; }
/* Clear all reads as a destructive tool button rather than an outlined chip:
   soft translucent red over the page, a subtle vertical gradient, a lit top edge
   and the same elevation as the toolbar buttons. */
.filter-chips .chip--clear {
  padding: 0 var(--praxis-space-16);
  color: var(--praxis-color-red-60);
  border: 0;
  background: linear-gradient(180deg, rgba(226, 45, 56, .09), rgba(226, 45, 56, .17));
  box-shadow: var(--px-tool-shadow), inset 0 .5px 0 rgba(255, 255, 255, .6);
  transition: background var(--praxis-motion-fast) var(--praxis-ease-default), box-shadow var(--praxis-motion-fast) var(--praxis-ease-default);
}
.filter-chips .chip--clear:hover {
  background: linear-gradient(180deg, rgba(226, 45, 56, .15), rgba(226, 45, 56, .24));
  box-shadow: var(--px-tool-shadow-hover), inset 0 .5px 0 rgba(255, 255, 255, .6);
}
.filter-chips .chip--clear:active { background: linear-gradient(180deg, rgba(226, 45, 56, .2), rgba(226, 45, 56, .28)); }
/* The bar's generic .chip__close is 24px; Clear all's leading × is a label
   glyph rather than a dismiss target, so it sits a size down. */
.filter-chips .chip--clear .material-symbols-rounded,
.filter-chips .chip--clear svg {
  /* inline-flex, not inline: width/height are ignored on a non-replaced inline
     box, so the glyph kept its 24px em square. Covers the SVG too — the Lucide
     converter swaps these ligatures at runtime. */
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; min-width: 20px;
  font-size: 20px !important; line-height: 1;
}
/* Dark chip contrast is handled by the theme-inverse rule further up (fill
   neutral-90, 70% white border, #fff label — 11.98:1). Worth recording why
   that rule matters beyond looks: praxis-core remaps --praxis-color-neutral-10
   (the old chip fill) to #222b39 but leaves --praxis-color-neutral-90 (the
   label) at #2a3846, so an active chip previously rendered dark-on-dark at
   1.19:1 — invisible, and against the project's hard WCAG 2.2 AA
   requirement. Inverting the fill fixes that at the same time. Don't
   reintroduce a colour-only override here; it would fight that rule. */

/* Dark: the lit edge flips to white-on-dark and the tint carries more weight,
   since a 9% red over a navy surface all but disappears. */
body[data-theme="dark"] .filter-chips .chip--clear {
  color: #ff8b93;
  border-color: rgba(255, 139, 147, .28);
  background: linear-gradient(180deg, rgba(255, 139, 147, .12), rgba(255, 139, 147, .2));
  box-shadow: var(--px-tool-shadow), inset 0 .5px 0 rgba(255, 255, 255, .08);
}
body[data-theme="dark"] .filter-chips .chip--clear:hover {
  background: linear-gradient(180deg, rgba(255, 139, 147, .18), rgba(255, 139, 147, .28));
  box-shadow: var(--px-tool-shadow-hover), inset 0 .5px 0 rgba(255, 255, 255, .08);
}
/* (was a 24px re-declaration here — the size now lives in the block above) */
.drawer-scrim { position: fixed; inset: 0px; background: rgba(32, 42, 53, 0.5); z-index: 80; opacity: 0; transition: opacity var(--praxis-motion-normal) var(--praxis-ease-default); }
.drawer-scrim[data-open] { opacity: 1; }
.filter-drawer { position: fixed; top: var(--praxis-space-24); right: var(--praxis-space-24); bottom: var(--praxis-space-24); width: min(480px, calc(100% - 2 * var(--praxis-space-24))); background: var(--px-surface); z-index: 90; display: flex; flex-direction: column; transform: translateX(calc(100% + var(--praxis-space-24))); transition: transform var(--praxis-motion-slow) var(--praxis-ease-default); box-shadow: rgba(32, 42, 53, 0.24) 0px 24px 64px, rgba(32, 42, 53, 0.12) 0px 8px 24px; border-radius: 20px; overflow: hidden; }
.filter-drawer[hidden] { display: none; }
.filter-drawer[data-open] { transform: none; }
@media (min-width: 1280px) {
  html[data-filter-mode="modal"] .filter-drawer { left: 0px; right: 0px; margin-inline: auto; width: min(960px, -96px + 100vw); height: fit-content; max-height: calc(100vh - 48px); margin-block: auto; transform: translateY(16px); opacity: 0; transition: transform var(--praxis-motion-normal) var(--praxis-ease-default),
                opacity var(--praxis-motion-normal) var(--praxis-ease-default); }
  html[data-filter-mode="modal"] .filter-drawer[data-open] { transform: none; opacity: 1; }
  html[data-filter-mode="modal"] .filter-drawer__resize { display: block; }
}

.filter-drawer__resize { display: none; position: absolute; left: 0px; right: 0px; height: 10px; cursor: ns-resize; z-index: 6; touch-action: none; }
.filter-drawer__resize--top { top: 0px; }
.filter-drawer__resize--bottom { bottom: 0px; }
.filter-drawer__head { display: flex; align-items: center; justify-content: space-between; padding: var(--praxis-space-16) var(--praxis-space-16) var(--praxis-space-4); }
.filter-drawer__head h2 { font-size: var(--praxis-type-size-xl); font-weight: 700; color: var(--praxis-color-neutral-90); }
.filter-drawer__toolbar { position: relative; display: flex; flex-direction: column; align-items: stretch; gap: var(--praxis-space-8); padding: var(--praxis-space-4) var(--praxis-space-16) var(--praxis-space-16); }
.filter-drawer__toolbar::after { content: ""; position: absolute; left: var(--praxis-space-16); right: var(--praxis-space-16); bottom: 0px; height: 1px; background: var(--praxis-color-neutral-20); }
.filter-toolbar-row { display: flex; align-items: center; gap: var(--praxis-space-8); width: 100%; min-width: 0px; flex: 1 1 auto; }
.filter-search-input { position: relative; width: 100%; max-width: 400px; min-width: 0px; height: 40px; }
.filter-toolbar-row .filter-search-input { flex: 1 1 auto; max-width: none; }
/* --px-field (#EEF1F4) is a step off white — legible recessed one level
   inside a --px-surface card, but the toolbar row places this field
   directly on --px-surface-2/--px-page, and the three are close enough (a
   few units per channel) that the field all but disappears. Give it the
   surface it's missing plus the shadow every other toolbar control already
   uses to lift off the band under it, rather than a darker field token. */
.filter-toolbar-row .filter-search-input input {
  background-color: var(--px-surface);
  box-shadow: var(--px-tool-shadow);
}
/* Needed alongside the rule above: an unscoped :hover would lose to the
   base surface override by specificity, and the field would stop reacting
   on hover inside this toolbar-row context. */
.filter-toolbar-row .filter-search-input input:hover {
  background-color: var(--px-surface);
  background-image: linear-gradient(0deg, var(--px-hover-btn), var(--px-hover-btn));
  box-shadow: var(--px-tool-shadow-hover);
}
/* Same reason as :hover above: without restating it here, the unscoped
   :focus ring's box-shadow would lose to this scope's own (0,2,1) box-shadow
   declarations by specificity and the focus ring would disappear. */
.filter-toolbar-row .filter-search-input input:focus {
  background-color: var(--px-surface);
  box-shadow: inset 0 0 0 1px var(--praxis-color-interactive-default),
    0 0 0 3px color-mix(in srgb, var(--praxis-color-interactive-default) 22%, transparent);
}
.filter-type-select { flex: 0 0 auto; min-width: 132px; font-size: var(--praxis-type-size-sm); }
.filter-type-select.is-active { border-color: var(--praxis-color-teal-50); background: var(--praxis-color-teal-10); color: var(--praxis-color-teal-80); }
.filter-type-select.is-active .material-symbols-rounded { color: var(--praxis-color-teal-70); }
.filter-search-input input { width: 100%; height: 40px; padding: 0 40px 0 var(--praxis-space-12); border: 0; border-radius: var(--praxis-radius-md); background-color: var(--px-field); font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-base); font-weight: 600; color: var(--praxis-color-text-primary); outline: none; }
.filter-search-input input:hover { background-color: var(--px-field-hover); }
.cf-input:hover { background-color: var(--px-field-hover); }
.filter-search-input input::placeholder { color: var(--praxis-color-text-secondary); font-weight: 500; opacity: 1; }
.filter-search-input input:focus { box-shadow: inset 0 0 0 1px var(--praxis-color-interactive-default),
    0 0 0 3px color-mix(in srgb, var(--praxis-color-interactive-default) 22%, transparent); }
.filter-search-input .material-symbols-rounded { position: absolute; right: var(--praxis-space-12); top: 50%; transform: translateY(-50%); color: var(--praxis-color-neutral-60); pointer-events: none; font-size: 20px; }
.filter-type-buttons { display: flex; width: 100%; max-width: 400px; border: 1px solid var(--praxis-color-neutral-20); border-radius: var(--praxis-radius-md); height: 40px; flex-shrink: 0; }
.filter-type-btn:first-child { border-radius: calc(var(--praxis-radius-md) - 1px) 0 0 calc(var(--praxis-radius-md) - 1px); }
.filter-type-btn:last-child { border-radius: 0 calc(var(--praxis-radius-md) - 1px) calc(var(--praxis-radius-md) - 1px) 0; }
.filter-type-btn {flex: 1 1 0%; height: 100%; display: inline-flex; align-items: center; justify-content: center; background: var(--px-surface-2); color: var(--praxis-color-neutral-90); border-right: 1px solid var(--praxis-color-neutral-20); transition: background var(--praxis-motion-fast) var(--praxis-ease-default); }
.filter-type-btn:last-child { border-right: 0px; }
.filter-type-btn:hover { background: var(--praxis-color-neutral-10); }
.filter-type-btn[aria-pressed="true"] { background: var(--praxis-color-teal-10); color: var(--praxis-color-teal-80); }
.filter-type-btn .material-symbols-rounded { font-size: 20px; }
.filter-drawer__main { flex: 1 1 0%; min-height: 0px; display: flex; flex-direction: column; }
.filter-drawer__body { flex: 1 1 0%; overflow-y: auto; padding: 0 var(--praxis-space-16) var(--praxis-space-16); display: flex; flex-direction: column; gap: 0px; }
.filter-drawer__body [data-filter-list] { display: flex; flex-direction: column; gap: 0px; }
.filter-row { display: flex; flex-direction: column; gap: 0px; padding: var(--praxis-space-16) 0; border-bottom: 1px solid var(--praxis-color-neutral-20); }
.filter-row[hidden] { display: none; }
.filter-more[hidden] { display: none; }
.filter-drawer__body [data-filter-list] .filter-row:last-child { border-bottom: 0px; }
.segmented--lg .segmented__opt { height: 40px; }
.filter-row__controls .segmented { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
.filter-row__controls .segmented__opt { justify-content: center; }
.filter-row__head { display: flex; align-items: center; gap: var(--praxis-space-8); }
/* No enclosure. The glyph used to sit in a filled, fully-rounded chip
   (neutral-05 at 12px radius on a 20px box), which read as a coloured circle —
   near-white in light mode, a distinctly dark disc in dark mode — competing
   with the chevron beside it for the same job. The glyph now stands on its own.
   Box is 20px because that is the size the icons actually render at: the 40px
   here never applied, since praxis-lucide.js replaces the ligature with an
   <svg class="icon icon--20"> whose own rule wins. Stating it explicitly keeps
   any span-rendered glyph (one that never got converted) the same size rather
   than leaving a 40px transparent gap. */
.filter-row__icon { display: grid; place-items: center; width: 20px; height: 20px; color: var(--praxis-color-neutral-90); font-size: 20px; flex-shrink: 0; }
.filter-row__title { font-size: var(--praxis-type-size-base); font-weight: 600; color: var(--praxis-color-neutral-90); margin: 0px; }
.filter-row__toggle { flex: 1 1 0%; display: flex; align-items: center; gap: var(--praxis-space-8); min-width: 0px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; padding: 0px; font: inherit; text-align: left; cursor: pointer; }
.filter-row__caret { font-size: 20px; color: var(--praxis-color-neutral-50); flex-shrink: 0; transition: color var(--praxis-motion-fast) var(--praxis-ease-default), transform var(--praxis-motion-normal) var(--praxis-ease-default); }
.filter-row__toggle:hover .filter-row__caret { color: var(--praxis-color-neutral-90); }
.filter-row__toggle[aria-expanded="true"] .filter-row__caret, .filter-more__head[aria-expanded="true"] .filter-row__caret { transform: rotate(90deg); }
.filter-row__fav, .filter-row__quick { display: grid; place-items: center; width: 38px; height: 38px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; border-radius: var(--praxis-radius-sm); color: var(--praxis-color-neutral-30); cursor: pointer; flex-shrink: 0; }
.filter-row__fav:hover { background: var(--praxis-color-neutral-10); color: var(--praxis-color-pink-60, #E30072); }
.filter-row__quick:hover { background: var(--praxis-color-neutral-10); color: var(--praxis-color-pink-60, #E30072); }
.filter-row__fav .material-symbols-rounded, .filter-row__quick .material-symbols-rounded { font-size: 23px; }
.filter-row__fav.is-on { color: var(--praxis-color-pink-60, #E30072); }
.filter-row__quick.is-on { color: var(--praxis-color-pink-60, #E30072); }
.filter-row__qf-badge { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 var(--praxis-space-8); border-radius: var(--praxis-radius-full); background: var(--praxis-color-pink-10, #FDE6F1); color: var(--praxis-color-pink-60, #E30072); font-size: var(--praxis-type-size-xs); font-weight: 700; white-space: nowrap; flex-shrink: 0; }
.filter-row--locked .filter-row__toggle { cursor: default; }
.filter-row--locked .filter-row__caret { visibility: hidden; }
.filter-row--locked .filter-row__title { color: var(--praxis-color-neutral-60); }
.filter-row__chips { display: flex; flex-wrap: wrap; gap: var(--praxis-space-8); margin-top: var(--praxis-space-8); margin-left: calc(20px + var(--praxis-space-8)); }
.filter-row__chips[hidden] { display: none; }
.frow-chip { display: inline-flex; align-items: center; gap: var(--praxis-space-4); padding: 3px 4px 3px var(--praxis-space-12); border-radius: var(--praxis-radius-full); background: var(--praxis-color-neutral-10); border: 1px solid var(--praxis-color-neutral-20); color: var(--praxis-color-neutral-80); font-size: var(--praxis-type-size-sm); font-weight: 600; }
.frow-chip__x { display: grid; place-items: center; width: 22px; height: 22px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; border-radius: 50%; color: var(--praxis-color-neutral-50); cursor: pointer; }
.frow-chip__x:hover { background: var(--praxis-color-neutral-20); color: var(--praxis-color-neutral-90); }
.frow-chip__x .material-symbols-rounded { font-size: 16px; }
/* minmax(0, 0fr), not 0fr: an fr track's implicit minimum is min-content, and
   .filter-row__controls carries padding-top:8px, which min-height:0 cannot
   remove — so a collapsed row's track floored at 8px instead of 0. That 8px sat
   below the header inside the row's 16px padding band, leaving the collapsed
   content visibly high in its container (12px above, 21px below). Flooring the
   track at 0 lets it close completely, so the header centres. The open state
   keeps its auto minimum so the controls still size to content. */
.filter-row__body { display: grid; grid-template-rows: minmax(0, 0fr); overflow: hidden; transition: grid-template-rows var(--praxis-motion-normal) var(--praxis-ease-default); }
.filter-row__body.is-open { grid-template-rows: 1fr; }
.filter-row__body > .filter-row__controls { min-height: 0px; overflow: hidden; opacity: 0; transition: opacity var(--praxis-motion-normal) var(--praxis-ease-default); }
.filter-row__body.is-open > .filter-row__controls { opacity: 1; }
.filter-row__controls { display: flex; flex-direction: column; align-items: stretch; gap: var(--praxis-space-8); padding: var(--praxis-space-8) 0 0 calc(20px + var(--praxis-space-8)); }
.filter-row__controls > * { width: 100%; }
.filter-row__value { min-width: 0px; display: flex; flex-direction: column; gap: var(--praxis-space-8); }
.filter-row__value > * { width: 100%; }
.filter-row__value:empty { display: none; }
.filter-date-range { display: flex; flex-direction: column; gap: var(--praxis-space-8); }
.filter-row__body > .filter-row__conditions { min-height: 0px; overflow: hidden; opacity: 0; transition: opacity var(--praxis-motion-normal) var(--praxis-ease-default); }
.filter-row__body.is-open > .filter-row__conditions { opacity: 1; }
.filter-row__conditions { display: flex; flex-direction: column; gap: var(--praxis-space-8); padding: var(--praxis-space-8) 0 0 calc(20px + var(--praxis-space-8)); }
.filter-cond { display: grid; grid-template-columns: minmax(120px, 0.8fr) minmax(150px, 1fr) auto; gap: var(--praxis-space-8); align-items: start; }
.filter-cond + .filter-cond { border-top: 1px dashed var(--praxis-color-neutral-20); padding-top: var(--praxis-space-8); }
.filter-cond .cf-value { min-width: 0px; }
.filter-row__add-cond { align-self: flex-start; display: inline-flex; align-items: center; gap: 4px; margin-top: 2px; height: 32px; padding: 0 var(--praxis-space-12) 0 var(--praxis-space-8); border: 1px dashed var(--praxis-color-neutral-30); border-radius: var(--praxis-radius-sm); background: transparent; color: var(--praxis-color-teal-70); font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-sm); font-weight: 600; cursor: pointer; transition: background var(--praxis-motion-fast) var(--praxis-ease-default), border-color var(--praxis-motion-fast) var(--praxis-ease-default); }
.filter-row__add-cond:hover { background: var(--praxis-color-teal-10); border-color: var(--praxis-color-teal-40); }
.filter-row__add-cond .material-symbols-rounded { font-size: 18px; }
@media (max-width: 639px) {
  .filter-cond { grid-template-columns: 1fr auto; }
  .filter-cond .cf-value { grid-column: 1 / -1; }
}

.filter-more { padding: var(--praxis-space-16) 0; }
.filter-more__head { width: 100%; display: flex; align-items: center; gap: var(--praxis-space-8); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; padding: 0px; font: inherit; text-align: left; cursor: pointer; }
.filter-more__count { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding: 0px; border-radius: var(--praxis-radius-full); background: var(--praxis-color-neutral-10); color: var(--praxis-color-neutral-70); font-size: var(--praxis-type-size-xs); font-weight: 700; flex-shrink: 0; }
.filter-more__body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--praxis-motion-slow) var(--praxis-ease-default); }
.filter-more.is-open .filter-more__body { grid-template-rows: 1fr; }
.filter-more__list { min-height: 0px; overflow: hidden; }
.filter-more__list .filter-row:first-child { margin-top: var(--praxis-space-8); border-top: 1px solid var(--praxis-color-neutral-20); }
.filter-more__list .filter-row:last-child { border-bottom: 0px; }
.filter-more__empty { padding: var(--praxis-space-12) 0 0; color: var(--praxis-color-neutral-50); font-size: var(--praxis-type-size-sm); }
.filter-select, .filter-date { display: flex; align-items: center; justify-content: space-between; gap: var(--praxis-space-8); height: 40px; padding: 0 var(--praxis-space-12); background: var(--px-surface-2); border: 1px solid var(--praxis-color-neutral-20); border-radius: var(--praxis-radius-md); font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-base); font-weight: 600; color: var(--praxis-color-neutral-90); text-align: left; cursor: pointer; min-width: 0px; }
.filter-select:hover, .filter-date:hover { background: var(--praxis-color-neutral-05); border-color: var(--praxis-color-neutral-30); }
.filter-select:focus-visible, .filter-date:focus-visible { outline: 2px solid var(--praxis-color-teal-50); outline-offset: 2px; }
.filter-select > span:first-child, .filter-date > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-select .material-symbols-rounded, .filter-date .material-symbols-rounded { color: var(--praxis-color-neutral-60); font-size: 20px; flex-shrink: 0; }
.filter-search { position: relative; height: 40px; min-width: 0px; }
.filter-search input { width: 100%; height: 40px; padding: 0 40px 0 var(--praxis-space-12); border: 1px solid var(--praxis-color-neutral-20); border-radius: var(--praxis-radius-md); background: var(--px-surface-2); font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-base); font-weight: 600; color: var(--praxis-color-neutral-90); outline: none; }
.filter-search input::placeholder { color: var(--praxis-color-neutral-50); font-weight: 500; }
.filter-search input:focus { border-color: var(--praxis-color-teal-50); box-shadow: 0 0 0 2px var(--praxis-color-teal-50); }
.filter-search .material-symbols-rounded { position: absolute; right: var(--praxis-space-12); top: 50%; transform: translateY(-50%); color: var(--praxis-color-neutral-60); pointer-events: none; font-size: 20px; }
.select-menu { background: var(--px-surface); border: 1px solid var(--praxis-color-neutral-20); border-radius: var(--praxis-radius-md); box-shadow: var(--px-overlay); padding: var(--praxis-space-4); max-height: 320px; overflow-y: auto; z-index: 200; }
.select-menu__opt { display: flex; align-items: center; width: 100%; padding: var(--praxis-space-8) var(--praxis-space-12); background: none; border: 0px; text-align: left; font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-base); font-weight: 600; color: var(--praxis-color-neutral-90); border-radius: var(--praxis-radius-sm); cursor: pointer; white-space: nowrap; }
.select-menu__opt:hover { background: var(--praxis-color-neutral-10); }
.select-menu__opt--active { background: var(--praxis-color-teal-10); color: var(--praxis-color-teal-80); }
.filter-date { position: relative; }
.filter-date__input { position: absolute; inset: 0px; width: 100%; height: 100%; opacity: 0; pointer-events: none; }
.onoff { display: inline-flex; align-items: center; gap: var(--praxis-space-8); cursor: pointer; justify-self: start; }
.onoff__label { font-size: var(--praxis-type-size-sm); font-weight: 600; color: var(--praxis-color-neutral-60); }
.onoff:has(.switch:checked) .onoff__label--on { color: var(--praxis-color-neutral-90); }
.onoff:not(:has(.switch:checked)) .onoff__label--off { color: var(--praxis-color-neutral-90); }
.filter-drawer__foot { display: flex; justify-content: flex-end; gap: var(--praxis-space-8); padding: var(--praxis-space-16); background: var(--praxis-color-neutral-10); border-top: 1px solid var(--praxis-color-neutral-20); box-shadow: rgba(32, 42, 53, 0.06) 0px -8px 16px, rgba(32, 42, 53, 0.04) 0px -2px 4px; position: relative; z-index: 1; }
.filter-drawer__foot .btn { flex: 1 1 auto; max-width: 150px; justify-content: center; white-space: nowrap; }
.filter-drawer__foot [data-restore-defaults] { margin-right: auto; flex: 0 0 auto; max-width: none; }
.filter-drawer__foot [data-restore-defaults][hidden] { display: none; }
.filter-drawer__foot [data-cf-clear-btn][hidden] { display: none; }
.filter-drawer__foot [data-action="close-filter-drawer"][hidden] { display: none; }
@media (max-width: 1399px) {
  .filter-controls { grid-template-columns: 1fr; }
  .filter-controls > .btn[data-action="open-filter-drawer"] { justify-self: start; }
}

/* LOCAL DELETION: `.filter-controls > .filter-toggle, .quick-filters` used to
   be hidden here from 1399px down. That handover now keys off
   body.tb-is-compact and lives in praxis-filters-local.css, so it survives a
   re-extract of this ported file. */

@media (max-width: 639px) {
  .filter-controls { grid-template-columns: 1fr; gap: var(--praxis-space-8); padding-bottom: var(--praxis-space-8); }
  .filter-controls > .btn[data-action="add-filter"], .filter-controls > .filter-toggle { justify-self: stretch; }
  .segmented { flex-shrink: 0; }
  .segmented__opt { padding: 0 var(--praxis-space-12); height: 32px; font-size: var(--praxis-type-size-xs); }
  .quick-filters { display: none; }
  .filter-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding: var(--praxis-space-8) 0; }
  .filter-chips::-webkit-scrollbar { display: none; }
  /* This is a scroll strip, not a wrapping row: chips keep their natural width
     and you swipe. max-width:100% from the base rule would make a long chip
     wrap to two lines instead of extending into the scroller, so it's lifted
     here — the wrapping behaviour is only wanted where the row actually wraps. */
  .filter-chips .chip { flex-shrink: 0; max-width: none; }
}

.quick-filters[hidden] { display: none !important; }
.filter-controls .filter-toggle[hidden] { display: none !important; }
.select-menu--multi { padding: 0px; display: flex; flex-direction: column; max-height: 340px; overflow: hidden; }
.select-menu__search { display: flex; align-items: center; gap: var(--praxis-space-8); padding: var(--praxis-space-8) var(--praxis-space-12); border-bottom: 1px solid var(--praxis-color-neutral-15); flex-shrink: 0; }
.select-menu__search .material-symbols-rounded { font-size: 18px; color: var(--praxis-color-neutral-50); }
.select-menu__search input { border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; outline: none; flex: 1 1 0%; min-width: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-sm); background: transparent; color: var(--praxis-color-neutral-90); }
.select-menu__search input::placeholder { color: var(--praxis-color-neutral-50); }
.select-menu__list { overflow-y: auto; padding: var(--praxis-space-4); }
.select-menu--multi .select-menu__opt { display: flex; align-items: center; gap: var(--praxis-space-8); width: 100%; text-align: left; }
.select-menu__check {width: 18px; height: 18px; border: 2px solid var(--praxis-color-neutral-30); border-radius: 4px; background: var(--px-surface-2); color: rgb(255, 255, 255); display: grid; place-items: center; flex-shrink: 0; transition: background var(--praxis-motion-fast) var(--praxis-ease-default), border-color var(--praxis-motion-fast) var(--praxis-ease-default); }
.select-menu__check .material-symbols-rounded { font-size: 14px; }
.select-menu__opt.is-checked .select-menu__check { background: var(--praxis-color-teal-60); border-color: var(--praxis-color-teal-60); }
.select-menu__empty { padding: var(--praxis-space-12); text-align: center; color: var(--praxis-color-neutral-50); font-size: var(--praxis-type-size-sm); }
.select-menu--tree { min-width: 260px; }
.select-menu__group-row { display: flex; align-items: center; }
.select-menu__group-row .select-menu__opt { flex: 1 1 0%; min-width: 0px; }
.select-menu__twist { width: 24px; height: 24px; flex-shrink: 0; display: grid; place-items: center; border-radius: var(--praxis-radius-sm); color: var(--praxis-color-neutral-60); transition: background var(--praxis-motion-fast) var(--praxis-ease-default); }
.select-menu__twist:hover { background: var(--praxis-color-neutral-10); }
.select-menu__twist--blank { pointer-events: none; }
.select-menu__twist .material-symbols-rounded { font-size: 18px; transition: transform var(--praxis-motion-fast) var(--praxis-ease-default); }
.select-menu__group.is-expanded .select-menu__twist .material-symbols-rounded { transform: rotate(90deg); }
.select-menu__kids { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--praxis-motion-normal) var(--praxis-ease-default); }
.select-menu__kids > div { overflow: hidden; min-width: 0px; }
.select-menu__group.is-expanded .select-menu__kids { grid-template-rows: 1fr; }
.select-menu__opt--child { padding-left: 46px; }
.select-menu__opt.is-mixed .select-menu__check { background: var(--praxis-color-teal-60); border-color: var(--praxis-color-teal-60); }
@media (min-width: 1280px) {
  .filter-row, .filter-more { padding: var(--praxis-space-12) 0; }
  .filter-row__controls { display: grid; grid-template-columns: minmax(0px, 0.6fr) minmax(0px, 1fr) minmax(0px, 0.4fr); align-items: start; }
  html[data-filter-mode="modal"] .filter-drawer__toolbar { flex-direction: row; align-items: center; }
}

.filter-type-btn { position: relative; }
.filter-type-btn::after { content: attr(data-tip); position: absolute; top: calc(100% + 6px); left: 50%; translate: -50%; z-index: 5; padding: 4px 8px; border-radius: var(--praxis-radius-sm, 6px); background: var(--praxis-color-neutral-90); color: rgb(255, 255, 255); font-size: var(--praxis-type-size-xs); font-weight: 600; white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity var(--praxis-motion-fast) var(--praxis-ease-default) .25s; }
.filter-type-btn:hover::after, .filter-type-btn:focus-visible::after { opacity: 1; }
.filter-view-switch { display: inline-flex; align-self: stretch; margin-left: auto; gap: 2px; padding: 3px; border: 1px solid var(--praxis-color-neutral-20); border-radius: var(--praxis-radius-md); background: var(--praxis-color-neutral-05, #f3f4f6); flex-shrink: 0; }
.filter-view-switch__opt { display: inline-flex; align-items: center; justify-content: center; gap: var(--praxis-space-8); height: 34px; padding: 0 var(--praxis-space-16); border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: calc(var(--praxis-radius-md) - 3px); background: transparent; color: var(--praxis-color-neutral-70); font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-sm); font-weight: 600; cursor: pointer; transition: background var(--praxis-motion-fast) var(--praxis-ease-default), color var(--praxis-motion-fast) var(--praxis-ease-default); }
.filter-view-switch__opt:hover { color: var(--praxis-color-neutral-90); }
.filter-view-switch__opt.is-active { background: var(--praxis-color-teal-60); color: rgb(255, 255, 255); box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 2px; }
.filter-view-switch__opt.is-active:hover { color: rgb(255, 255, 255); }
.filter-view-switch__opt .material-symbols-rounded { font-size: 18px; }

/* =====================================================================
   PRAXIS — Standard / Custom takes the workspace segmented switch:
   .segswitch in index.html, the control the Assignments and Recently Created
   Items cards use (.asg__tabs / .rci__range are only size modifiers on it).
   Values are copied from there so the two read as one component.

   An earlier pass here matched the Create New flyout's .cn-seg instead. That
   was the wrong reference: .cn-seg is a flat chip track with a single flat
   shadow, while the workspace switch is a RECESSED groove — inset shadow plus
   hairline — with the selected option raised on a ring and two drop shadows.
   Same silhouette, visibly different material.

   Geometry is untouched (34px options, 16px padding, 18px icons); only the
   surface treatment moves.

   The selected label is plain ink: the lifted pill and its shadow already carry
   the state, so colour doesn't need to repeat it, and ink keeps the label from
   reading as a link. .cn-seg was moved to match, so this is now one treatment
   across all four segmented controls in the system.

   Specificity: further down this file there is a dark-mode colour correction
   for the old teal fill — body[data-theme="dark"] .filter-view-switch__opt
   .is-active, which sets --px-primary-fg (#08313a, near-black teal, chosen to
   sit on a saturated teal button). A [data-variant] prefix does NOT beat it:
   both compute to (0,3,1), so the later one wins and the label came out
   #08313a on the #192336 pill — 1.13:1, invisible. The [data-theme] variants
   below take it to (0,4,1)/(0,5,1) so this treatment holds in dark too.
   ===================================================================== */
body[data-variant="praxis"] .filter-view-switch {
  border: 0; padding: 3px; background: var(--px-chip); border-radius: 11px;
  box-shadow: inset 0 1px 2px rgba(16,36,58,.10), inset 0 0 0 .5px rgba(16,36,58,.06);
}
body[data-variant="praxis"] .filter-view-switch__opt { color: var(--praxis-color-text-secondary); border-radius: 8px; }
body[data-variant="praxis"] .filter-view-switch__opt:hover { color: var(--praxis-color-text-primary); }
body[data-variant="praxis"] .filter-view-switch__opt.is-active,
body[data-variant="praxis"] .filter-view-switch__opt.is-active:hover {
  background: var(--px-surface);
  color: var(--praxis-color-text-primary);
  box-shadow: 0 0 0 .5px rgba(16,36,58,.14), 0 1px 2px rgba(16,36,58,.10), 0 2px 5px -2px rgba(16,36,58,.14);
}
/* Dark: groove darkens, raised option takes the elevated surface + lit edge. */
body[data-variant="praxis"][data-theme="dark"] .filter-view-switch {
  background: rgba(0,0,0,.22);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.45), inset 0 0 0 .5px rgba(255,255,255,.05);
}
body[data-variant="praxis"][data-theme="dark"] .filter-view-switch__opt.is-active,
body[data-variant="praxis"][data-theme="dark"] .filter-view-switch__opt.is-active:hover {
  background: var(--px-surface-2);
  color: var(--praxis-color-white);
  box-shadow: 0 0 0 .5px rgba(255,255,255,.14), 0 1px 3px rgba(0,0,0,.5), inset 0 .5px 0 rgba(255,255,255,.10);
}
.custom-builder { position: absolute; inset: 0px; z-index: 3; background: var(--px-surface, var(--praxis-color-white)); display: flex; flex-direction: column; }
.custom-builder[hidden] { display: none; }
.custom-builder__head { position: relative; display: flex; align-items: flex-start; justify-content: space-between; gap: var(--praxis-space-16); padding: var(--praxis-space-12) var(--praxis-space-16); }
.custom-builder__head::after { content: ""; position: absolute; left: var(--praxis-space-16); right: var(--praxis-space-16); bottom: 0px; height: 1px; background: var(--praxis-color-neutral-20); }
.custom-builder__head h3 { font-size: var(--praxis-type-size-base); font-weight: 700; color: var(--praxis-color-neutral-90); }
.custom-builder__head p { font-size: var(--praxis-type-size-xs); color: var(--praxis-color-neutral-60); margin-top: 2px; max-width: 520px; }
.cf-example-btn { border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; color: var(--praxis-color-teal-70); font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-sm); font-weight: 700; cursor: pointer; white-space: nowrap; padding: var(--praxis-space-4) 0; }
.cf-example-btn:hover { text-decoration: underline; }
.custom-builder__tree { flex: 1 1 0%; overflow-y: auto; padding: var(--praxis-space-12) var(--praxis-space-16); }
.cf-group { border: 1px solid var(--praxis-color-neutral-15); border-radius: var(--praxis-radius-md); background: var(--praxis-color-neutral-05); padding: var(--praxis-space-8) var(--praxis-space-12) var(--praxis-space-12); }
.cf-group--d0 { background: rgb(255, 255, 255); }
.cf-group .cf-group { margin-top: 0px; }
.cf-group__bar { display: flex; align-items: center; gap: var(--praxis-space-8); padding-bottom: var(--praxis-space-8); }
.cf-group__hint { font-size: var(--praxis-type-size-xs); font-weight: 700; letter-spacing:normal; color: var(--praxis-color-neutral-40); flex: 1 1 0%; }
.cf-group__children { display: flex; flex-direction: column; gap: var(--praxis-space-8); }
.cf-empty { font-size: var(--praxis-type-size-sm); color: var(--praxis-color-neutral-50); padding: var(--praxis-space-8); }
.cf-join { align-self: flex-start; display: inline-flex; gap: 2px; padding: 2px; margin-left: var(--praxis-space-12); background: var(--praxis-color-neutral-10); border-radius: var(--praxis-radius-full); }
.cf-join button { border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: 11px; font-weight: 800; letter-spacing: 0.04em; padding: 3px 12px; border-radius: var(--praxis-radius-full); color: var(--praxis-color-neutral-50); cursor: pointer; transition: background var(--praxis-motion-fast) var(--praxis-ease-default), color var(--praxis-motion-fast) var(--praxis-ease-default); }
.cf-join button:hover { color: var(--praxis-color-neutral-80); }
.cf-join .cf-join__and.is-active { background: var(--praxis-color-teal-70); color: rgb(255, 255, 255); }
.cf-join .cf-join__or.is-active { background: rgb(176, 23, 92); color: rgb(255, 255, 255); }
.cf-cond { display: grid; grid-template-columns: auto minmax(150px, 1fr) minmax(130px, 0.9fr) minmax(150px, 1fr) auto; gap: var(--praxis-space-8); align-items: start; padding: var(--praxis-space-8) 0; }
.cf-cond, .cf-group { position: relative; }
.cf-cond__lead { display: inline-flex; align-items: center; gap: 2px; padding-top: 9px; }
.cf-drag { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 24px; color: var(--praxis-color-neutral-40); cursor: grab; }
.cf-drag:hover { color: var(--praxis-color-neutral-70); }
.cf-drag:active { cursor: grabbing; }
.cf-drag .material-symbols-rounded { font-size: 18px; }
.cf-select { display: inline-flex; align-items: center; }
.cf-select input { width: 15px; height: 15px; cursor: pointer; accent-color: var(--praxis-color-teal-60); margin: 0px; }
.cf-cond.is-selected { outline: 2px solid var(--praxis-color-teal-40); outline-offset: 3px; border-radius: var(--praxis-radius-sm); }
.cf-group__bar .cf-drag { height: 22px; }
.cf-dragging { opacity: 0.4; }
.cf-drop-before::before, .cf-drop-after::after { content: ""; position: absolute; left: 0px; right: 0px; height: 2px; background: var(--praxis-color-teal-60); border-radius: 2px; z-index: 2; }
.cf-drop-before::before { top: -5px; }
.cf-drop-after::after { bottom: -5px; }
.cf-group-selected { color: var(--praxis-color-teal-70) !important; border-color: var(--praxis-color-teal-50) !important; }
.cf-group-selected:hover { background: var(--praxis-color-teal-10) !important; }
.cf-pick { min-width: 0px; }
.cf-value { min-width: 0px; display: flex; flex-direction: column; gap: var(--praxis-space-8); }
.cf-value:empty { display: none; }
.cf-input { height: 40px; padding: 0 var(--praxis-space-12); border: 0; border-radius: var(--praxis-radius-md); background-color: var(--px-field); font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-sm); color: var(--praxis-color-text-primary); outline: none; width: 100%; }
.cf-input:focus { outline: none; box-shadow: inset 0 0 0 1px var(--praxis-color-interactive-default),
    0 0 0 3px color-mix(in srgb, var(--praxis-color-interactive-default) 22%, transparent); }
@media (max-width: 1279px) {
  .filter-select, .filter-date, .filter-search, .filter-search input, .cf-input { height: 44px; }
}

.cf-date-range { display: flex; flex-direction: column; gap: var(--praxis-space-8); }
.cf-remove { display: grid; place-items: center; width: 32px; height: 32px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; background: transparent; border-radius: var(--praxis-radius-full); color: var(--praxis-color-neutral-40); cursor: pointer; flex-shrink: 0; }
.cf-remove:hover { background: var(--praxis-color-neutral-10); color: rgb(192, 36, 74); }
.cf-remove .material-symbols-rounded { font-size: 18px; }
.cf-group__add { display: flex; gap: var(--praxis-space-8); padding-top: var(--praxis-space-8); }
.cf-group__add button { display: inline-flex; align-items: center; gap: 4px; border: 1px dashed var(--praxis-color-neutral-30); background: transparent; border-radius: var(--praxis-radius-full); padding: 5px 12px; font-style: inherit; font-variant: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-size: var(--praxis-type-size-xs); font-weight: 700; color: var(--praxis-color-neutral-70); cursor: pointer; }
.cf-group__add button:hover { border-color: var(--praxis-color-teal-50); color: var(--praxis-color-teal-70); background: var(--praxis-color-teal-10); }
.cf-group__add .material-symbols-rounded { font-size: 16px; }
.filter-expr { flex-shrink: 0; border-top: 1px solid var(--praxis-color-neutral-20); padding: var(--praxis-space-8) var(--praxis-space-16); background: var(--praxis-color-neutral-05); max-height: 110px; overflow-y: auto; }
.filter-expr[hidden] { display: none; }
.filter-expr__label { font-size: 10px; font-weight: 700; letter-spacing:normal; color: var(--praxis-color-neutral-50); }
.filter-expr__body { font-size: var(--praxis-type-size-sm); line-height: 1.7; color: var(--praxis-color-neutral-90); margin-top: 2px; }
/* The expression readout's parts, as emitted by formatExpr(). Each is its own
   element so a consumer can give it a shape — see the note in praxis-filters.js.
   .cfx-field replaced a bare <b> in 0.1.12; the weight here is what keeps that
   appearance for anyone not restyling it. */
.cfx-field { font-weight: 700; }
/* Spacing between the parts is a margin, not the literal spaces formatExpr used
   to emit: a space glyph is whatever width the font makes it, which cannot be
   lined up with anything, and it collapses unpredictably once a part becomes an
   inline-flex box. 2px a side reproduces the ~4px gap the spaces gave at the
   readout's size. A consumer painting the parts as chips will want more — set
   margin-inline on these same classes. */
.cfx-field, .cfx-opword, .cfx-val, .cfx-bool, .cfx-incomplete { margin-inline: 2px; }
.cfx-field:first-child { margin-left: 0; }
.cfx-bool { font-weight: 800; font-size: var(--praxis-type-size-xs); padding: 1px 7px; border-radius: var(--praxis-radius-full); }
.cfx-bool--and { background: var(--praxis-color-teal-10); color: var(--praxis-color-teal-80); }
.cfx-bool--or { background: rgb(251, 227, 232); color: rgb(176, 23, 92); }
.cfx-paren { font-weight: 800; color: rgb(232, 163, 61); }
.cfx-val { color: var(--praxis-color-teal-80); font-weight: 600; }
.cfx-opword { color: var(--praxis-color-neutral-60); }
.cfx-incomplete { color: var(--praxis-color-neutral-40); font-style: italic; }
/* A condition whose value is not chosen yet. It rendered as a plain .cfx-val
   before 0.1.12 — teal and semibold, i.e. indistinguishable from a value the
   user had actually picked, and reading as a value literally named "…". It takes
   the incomplete treatment instead. */
.cfx-val--pending { color: var(--praxis-color-neutral-40); font-weight: 400; font-style: italic; }
.custom-builder__actions { display: flex; justify-content: flex-end; gap: var(--praxis-space-8); padding: var(--praxis-space-12) var(--praxis-space-16); border-top: 1px solid var(--praxis-color-neutral-15); }
.custom-builder__actions .btn { max-width: 220px; justify-content: center; }
.custom-builder__actions [data-action="cf-clear"] { margin-right: auto; }
.custom-summary { border: 1px solid var(--praxis-color-teal-30); border-radius: var(--praxis-radius-md); background: var(--praxis-color-teal-10); padding: var(--praxis-space-12); margin: var(--praxis-space-12) 0; }
.custom-summary[hidden] { display: none; }
.custom-summary__head { display: flex; align-items: center; gap: var(--praxis-space-8); }
.custom-summary__head .material-symbols-rounded { font-size: 19px; color: var(--praxis-color-teal-70); }
.custom-summary__head h3 { font-size: var(--praxis-type-size-base); font-weight: 700; color: var(--praxis-color-neutral-90); flex: 1 1 0%; }
.custom-summary__count { font-size: var(--praxis-type-size-xs); font-weight: 700; color: var(--praxis-color-teal-80); background: rgb(255, 255, 255); border: 1px solid var(--praxis-color-teal-30); border-radius: var(--praxis-radius-full); padding: 2px 10px; }
.custom-summary__expr { background: rgb(255, 255, 255); border: 1px solid var(--praxis-color-teal-20); border-radius: var(--praxis-radius-sm); padding: var(--praxis-space-8) var(--praxis-space-12); margin-top: var(--praxis-space-8); font-size: var(--praxis-type-size-sm); line-height: 1.7; color: var(--praxis-color-neutral-90); }
.custom-summary__actions { display: flex; gap: var(--praxis-space-8); margin-top: var(--praxis-space-8); }
.custom-summary__actions .btn { padding: 6px 16px; }
.filter-chips .chip--custom { border-color: var(--praxis-color-teal-50); background: var(--praxis-color-teal-10); color: var(--praxis-color-teal-80); }
.filter-chips .chip--custom .material-symbols-rounded:first-child { font-size: 16px; }
@media (max-width: 1279px) {
  .cf-cond { grid-template-columns: auto 1fr auto; }
  .cf-cond .cf-pick[data-role="cf-op"], .cf-cond .cf-value { grid-column: 2; }
}

.filter-drawer:has(.custom-builder:not([hidden])) .filter-drawer__body { display: none; }
.filter-drawer:has(.custom-builder:not([hidden])) .filter-toolbar-row { display: none; }
.custom-builder:not([hidden]) { position: static; flex: 1 1 0%; min-height: 0px; }
@media (min-width: 1280px) {
  html[data-filter-mode="modal"] .filter-drawer:has(.custom-builder:not([hidden])) { height: fit-content; max-height: calc(-48px + 100vh); }
}

.select-menu { animation: motion-menu-in var(--praxis-motion-normal) var(--praxis-ease-default); transform-origin: left top; }
.quick-filters:not([hidden]) { animation: motion-rise-in var(--praxis-motion-slow) var(--praxis-ease-default); }
.quick-filters__scroller .qfilter { animation: motion-rise-in var(--praxis-motion-slow) var(--praxis-ease-default) backwards; }
.quick-filters__scroller .qfilter:nth-child(2) { animation-delay: 40ms; }
.quick-filters__scroller .qfilter:nth-child(3) { animation-delay: 80ms; }
.quick-filters__scroller .qfilter:nth-child(4) { animation-delay: 120ms; }
.quick-filters__scroller .qfilter:nth-child(5) { animation-delay: 160ms; }
.filter-chips .chip { animation: motion-fade-in var(--praxis-motion-normal) var(--praxis-ease-default); }
.custom-builder:not([hidden]), .custom-summary:not([hidden]) { animation: motion-rise-in var(--praxis-motion-normal) var(--praxis-ease-default); }
.filter-row__chips:not([hidden]), .filter-drawer__foot [data-restore-defaults]:not([hidden]) { animation: motion-fade-in var(--praxis-motion-normal) var(--praxis-ease-default); }
.cf-enter { animation: motion-rise-in var(--praxis-motion-normal) var(--praxis-ease-default); }
.cf-enter--controls .cf-pick[data-role="cf-op"], .cf-enter--controls .cf-value > * { animation: motion-rise-in var(--praxis-motion-normal) var(--praxis-ease-default); }
.cf-exit { animation: motion-shrink-out 140ms var(--praxis-ease-default) forwards; pointer-events: none; }
.chip, .filter-type-btn, .filter-view-switch__opt, .cf-example-btn, .filter-toggle, .qfilter__check, .select-menu__check { transition: background var(--praxis-motion-fast) var(--praxis-ease-default),
    background-color var(--praxis-motion-fast) var(--praxis-ease-default),
    border-color var(--praxis-motion-fast) var(--praxis-ease-default),
    color var(--praxis-motion-fast) var(--praxis-ease-default),
    box-shadow var(--praxis-motion-fast) var(--praxis-ease-default),
    opacity var(--praxis-motion-fast) var(--praxis-ease-default),
    transform var(--praxis-motion-fast) var(--praxis-ease-default); }
.chip:active, .filter-type-btn:active, .filter-view-switch__opt:active, .cf-example-btn:active, .filter-toggle:active { transform: scale(0.97); }
.qfilter__more, .select-menu__opt, .filter-more__head { transition: background var(--praxis-motion-fast) var(--praxis-ease-default),
    border-color var(--praxis-motion-fast) var(--praxis-ease-default),
    border-bottom-color var(--praxis-motion-fast) var(--praxis-ease-default),
    color var(--praxis-motion-fast) var(--praxis-ease-default); }
.filter-search-input input { transition: background-color var(--praxis-motion-fast) var(--praxis-ease-default),
              box-shadow var(--praxis-motion-fast) var(--praxis-ease-default); }

/* ---------------------------------------------------------------------
   Checkbox faces: show the tick only when actually checked.

   Upstream relied on the tick being white-on-white and only becoming visible
   once the box filled teal. That breaks the moment the box sits on a dark
   surface (Praxis dark theme), where a white tick reads as permanently
   checked. Drive visibility explicitly off the state class instead — which
   also survives praxis-lucide swapping the glyph for an SVG.
   --------------------------------------------------------------------- */
.qfilter__check > *,
.select-menu__check > * { opacity: 0; transition: opacity var(--praxis-motion-fast) var(--praxis-ease-default); }
.qfilter__row--selected .qfilter__check > *,
.select-menu__opt.is-checked .select-menu__check > *,
.select-menu__opt.is-mixed .select-menu__check > * { opacity: 1; }

/* ---------------------------------------------------------------------
   Quick-filter card spacing

   The source stylesheet leaned on its host page's global `ul, ol { margin:0;
   padding:0; list-style:none }` reset. search-page.html doesn't load
   praxis-chrome-legacy.css, so the card's <ul> kept UA defaults — a 40px inline
   start padding that pushed every checkbox 44px right of the card title, plus
   14px block margins that made the vertical rhythm lumpy. Reset it here so the
   component doesn't depend on the host having a reset at all.
   --------------------------------------------------------------------- */
.qfilter__list { margin: 0; padding: 0; list-style: none; }

/* Rows: content aligns flush with the card title, while the hover background
   still bleeds into the card padding so the target doesn't look cramped. */
.qfilter__row { padding: var(--praxis-space-4); margin-inline: calc(var(--praxis-space-4) * -1); }

/* Value labels at 14px, matching the card title */
.qfilter__row { font-size: var(--praxis-type-size-base); }

/* "See More" keeps its own even spacing; the cards are equal-height grid items,
   so it stays bottom-anchored across the row rather than floating mid-card. */
.qfilter__more { padding: var(--praxis-space-4); margin-inline: calc(var(--praxis-space-4) * -1); text-align: left; }

/* ---------------------------------------------------------------------
   Page gutter — the strip and the chip bar line up with whatever inset the
   host uses for its page header / toolbar / results. Defaults to flush so the
   component drops into any container; the host opts in.
   --------------------------------------------------------------------- */
.quick-filters,
.filter-chips { padding-inline: var(--praxis-filters-gutter, 0); }

/* ---------------------------------------------------------------------
   Quick-filter card head

   Same class of leak as the <ul> above: .qfilter__title is an <h3> and the
   source relied on the host's global heading reset. Without it the UA's
   `margin: 1em 0` added 14px above AND below the title — pushing it 27px down
   from the card top and opening a 22px gulf before the first value, which is
   what was forcing the cards tall. The close button likewise inherited the
   host's 36px .icon-btn instead of the intended 20px --xs size.
   --------------------------------------------------------------------- */
.qfilter__title { margin: 0; }
.qfilter__close { width: 20px; height: 20px; flex-shrink: 0; padding: 0; }
.qfilter__close .material-symbols-rounded,
.qfilter__close svg { width: 16px; height: 16px; font-size: 16px; }

/* Head sits tight to the top; values follow close behind it */
.qfilter { padding: var(--praxis-space-12) var(--praxis-space-12) var(--praxis-space-12) var(--praxis-space-16); gap: var(--praxis-space-4); }
.qfilter__head { align-items: flex-start; }

/* Rows realign to the card's roomier left padding */
.qfilter__row,
.qfilter__more { margin-inline: calc(var(--praxis-space-4) * -1); }

/* =====================================================================
   ACCORDION MOTION + HIT TARGET

   1. Easing. The whole component runs on --praxis-ease-default,
      cubic-bezier(.32,.72,0,1). That curve is extremely front-loaded — it
      reaches ~77% progress in the first quarter of its duration, then
      crawls to rest. On a hover colour it's imperceptible, but driving
      grid-template-rows 0fr→1fr it reads as a springy snap-and-settle:
      the row flies open, overshoots the eye, then eases in. Swap the
      opening rows (and the caret that tracks them) onto an even
      decelerate. Nothing else in the panel changes — the drawer slide,
      hovers and press states keep the house curve.

      Note this is a perceptual bounce, not a literal one: neither curve in
      the system overshoots mathematically (no y control point exceeds 1).
      Duration is unchanged at --d-normal; only the distribution moves.

   2. Hit target. .filter-row__toggle only spans caret + icon + title, so
      the row's 16px padding band, the header gaps and the quick-filter
      badge were all dead space — a click that visibly landed "on the row"
      did nothing. praxis-filters.js now forwards those to the button; this
      block supplies the matching affordance.
   ===================================================================== */

.filter-controls,
.quick-filters,
.filter-chips,
.filter-drawer,
.filter-menu,
.drawer-scrim,
.select-menu {
  /* Even decelerate — no snap at the head of the curve. */
  --ease-accordion: cubic-bezier(.4, 0, .2, 1);
}

.filter-row__body { transition: grid-template-rows var(--praxis-motion-normal) var(--ease-accordion); }
.filter-row__body > .filter-row__controls,
.filter-row__body > .filter-row__conditions { transition: opacity var(--praxis-motion-normal) var(--ease-accordion); }
.filter-row__caret { transition: color var(--praxis-motion-fast) var(--praxis-ease-default), transform var(--praxis-motion-normal) var(--ease-accordion); }
/* The "More Filters" roll-up is the same interaction one level up, so it
   tracks the rows rather than keeping the springier curve. */
.filter-more__body { transition: grid-template-rows var(--praxis-motion-slow) var(--ease-accordion); }

/* =====================================================================
   QUICK-FILTER BOLT — filled when on

   .is-on only changed the glyph's colour, so a pinned quick filter read as
   "pink outline" against an "unset grey outline" — a hue change carrying
   the whole state, which is weak at 23px and invisible to anyone who
   can't separate the two hues. Fill the glyph instead, so the state also
   reads as weight.

   praxis-lucide.js rewrites the Material ligature `bolt` into a Lucide
   `zap` <svg>, which is stroke-built (fill="none" stroke="currentColor"),
   so this is a fill change rather than the Material FILL axis. The font
   glyph is covered too, in case the Lucide pass hasn't run or is absent —
   the page loads the FILL 0..1 axis, so 'FILL' 1 is available there.

   Both use currentColor, so the fill follows whatever pink the theme has
   already resolved — #E30072 in light, the brightened #FF7AB8 in dark —
   and the treatment is identical in both. */
.filter-row__quick.is-on svg,
.filter-row__quick.is-on svg * { fill: currentColor; }
.filter-row__quick.is-on .material-symbols-rounded { font-variation-settings: 'FILL' 1; }

/* ---- The press bounce: the global scale-down on wide controls -------
   praxis-core.css gives every control a press affordance: hover lifts,
   press scales down over 150ms. It splits by footprint — .95 for "small
   controls: icon buttons and compact pills", .97 for larger surfaces,
   because (its words) "Larger surfaces travel less, or the shrink reads
   as a jump."

   The split keys off class, though, and the bucket for .95 is a bare
   `button` selector. Several controls here are full-width rows that carry
   no .btn-family class, so they land in the SMALL bucket and take the
   largest scale of anything on the page:

     .filter-more__head    928px wide  → shrinks ~46px on press
     .filter-row__toggle   836px wide  → shrinks ~42px on press
     .filter-select        491px wide  → shrinks ~25px on press
     .filter-date          491px wide  → shrinks ~25px on press

   On the accordion headers that's the visible fault: press Status or
   Priority and the chevron and title lurch inward, spring back over
   150ms on the house curve, and only then does the row open.

   praxis-core writes those rules with :where() at specificity 0 so a
   component can opt out — "A component that wants different press
   behaviour — or none — overrides it with any class rule." That invitation
   undersells what's needed, though: :where() is 0, but the rest of the
   selector is `:active:not(:disabled):not([aria-disabled="true"])`, and
   :not() contributes its argument's specificity. The core rule is
   therefore (0,3,0), and a plain `.filter-row__toggle:active` — (0,2,0) —
   loses to it even when declared later. Verified in-engine, not assumed.

   Hence the descendant + :not() below: (0,4,0), which beats it outright
   rather than depending on load order. These are rows and fields, not
   buttons; they already signal press through hover and the caret.
   Genuinely small controls in the panel (the bolt, bookmark, close and
   twist icons) keep the affordance. */
.filter-drawer .filter-row__toggle:active:not(:disabled),
.filter-drawer .filter-more__head:active:not(:disabled),
.filter-drawer .filter-select:active:not(:disabled),
.filter-drawer .filter-date:active:not(:disabled) { transform: none; }

/* ---- The panel resizing under the accordion -------------------------
   Easing was only half the story. In modal mode the panel is
   `height: fit-content` AND vertically centred (`margin-block: auto`), so
   every row that expands grows the dialog — and a centred box grows BOTH
   ways. Measured on a 980px viewport, opening one row grew the panel 42px:
   the top edge rose 21px while the bottom dropped 21px, over the same
   180ms the row was easing open. The whole dialog breathes outward from
   its centre on every click. That movement is what reads as a bounce, and
   no easing change can fix it — initModalResize() already documents the
   behaviour ("The modal is centered, so it grows both ways").

   So take content out of the height equation: give the panel a stable
   height and let .filter-drawer__body (already overflow-y:auto) absorb the
   growth by scrolling. Rows then open in place against a dialog that never
   moves.

   An explicit height, not max-height, now drives the sheet. The drag
   handles keep working unchanged — initModalResize() writes that same
   property, and its `grow * 2` still holds because a centred box of fixed
   height moves each edge by half the delta. The default tracks the height
   the panel naturally settled at, so its resting size is unchanged. */
@media (min-width: 1280px) {
  html[data-filter-mode="modal"] .filter-drawer,
  html[data-filter-mode="modal"] .filter-drawer:has(.custom-builder:not([hidden])) {
    /* var(), not a bare 720px: initModalResize() writes --filter-modal-h on the
       drawer's inline style on every pointermove, and until 0.1.12 nothing read
       it — a grep for the property across the package returned exactly one hit,
       the write. So the drag handles bound, fired and computed correctly while
       the panel never moved. The variable was orphaned by the stable-height
       change documented above, which replaced this rule's max-height with an
       explicit height and hardcoded the 720px the resting size needs. 720px is
       now the fallback, so that resting size is unchanged. The handler already
       clamps to [360, innerHeight - 48]; the min() here is the same limit from
       the CSS side. */
    height: min(var(--filter-modal-h, 720px), calc(100vh - 48px));
    max-height: calc(100vh - 48px);
  }
}

/* The whole header row is now a hit target, so it takes the pointer and
   drives the caret's hover state — previously both were scoped to the
   inner button. Locked rows (pinned as quick filters) don't expand, so
   they keep the default cursor and no hover response. */
.filter-row__head { cursor: pointer; }
.filter-row__head:hover .filter-row__caret { color: var(--praxis-color-neutral-90); }
.filter-row--locked .filter-row__head { cursor: default; }
.filter-row--locked .filter-row__head:hover .filter-row__caret { color: var(--praxis-color-neutral-50); }

/* =====================================================================
   DARK THEME

   The ported v1.5 stylesheet paints with RAW SCALE tokens
   (--praxis-color-neutral-90, --praxis-color-teal-80, …) rather than the
   semantic text/surface tokens, because its source only ever ran in light.
   praxis-core's dark theme remaps just the low end of that scale —
   neutral-05/10/15/20, the surface steps — and leaves neutral-30..100 and
   the whole teal/red/pink tint range at their light values. So in dark:

     • every `color: var(--praxis-color-neutral-90)` painted #2a3846 (dark
       navy) onto a #192336 modal — 1.2:1, illegible. That hit the modal
       title, every filter-row title, all inputs, selects and menu options.
     • the teal-10 "selected" tints (#e8f3f3, near-white) glared as light
       blocks on the dark panel.
     • three panels hardcode rgb(255,255,255) and rendered pure white.

   Rather than rewrite 500 lines of ported rules (and risk regressing
   light), remap the scale itself inside the filter roots — same scope as
   the bridge block at the top of this file, so it cannot leak into a host
   page. That corrects the bulk of the component in one pass; the sections
   below then fix only the rules where a token is used as a FILL behind
   light ink, which a straight scale flip would invert the wrong way.

   Values follow the Mazlan dark DNA already in praxis-core: #e7ebf1 ink,
   the #29D2D7/#5CE0E5 cyan accent in place of light-mode teal, and dark
   ink (--px-primary-fg) on any bright cyan fill. All text pairings here
   clear WCAG 2.2 AA (4.5:1) against the surface they sit on, and control
   borders clear 3:1 for 1.4.11.
   ===================================================================== */

body[data-theme="dark"] :is(.filter-controls,
.quick-filters,
.filter-chips,
.filter-drawer,
.filter-menu,
.drawer-scrim,
.select-menu) {
  /* Ink. The scale is ordered light→dark in the source, so it inverts:
     the high steps become the brightest ink. */
  --praxis-color-neutral-100:#f2f5f9;
  --praxis-color-neutral-90:#e7ebf1;   /* primary ink — titles, inputs, options */
  --praxis-color-neutral-80:#cfd8e3;
  --praxis-color-neutral-70:#b7c3d0;
  --praxis-color-neutral-60:#9aa7b4;   /* secondary ink, trailing icons */
  --praxis-color-neutral-50:#8b98a6;   /* placeholders, empty states — 5.4:1 */
  --praxis-color-neutral-40:#7e8c9b;   /* faint hints, drag handles — 4.6:1 */
  /* Borders and "off" icon controls. Translucent white so it rides whatever
     surface it lands on (modal, recessed group, footer) at a consistent
     weight; .42 lands ~3.4:1 for the checkbox borders and the unset
     favourite/quick-filter icons, clearing 1.4.11's 3:1 for controls. */
  --praxis-color-neutral-30:rgba(255,255,255,.42);

  /* Accent. Light-mode teal is far too dark to read on the panel, so the
     family shifts to the cyan praxis-core already uses for dark accents.
     The low steps stay translucent — a flat tint would occlude the surface
     tier it sits on and flatten the modal's elevation. teal-30/40 are
     referenced by .custom-summary and .cf-cond.is-selected but were never
     defined at any level, so those declarations were dropping as invalid;
     defining them here also repairs that in dark. */
  --praxis-color-teal-10:rgba(41,210,215,.14);
  --praxis-color-teal-20:rgba(41,210,215,.22);
  --praxis-color-teal-30:rgba(41,210,215,.32);
  --praxis-color-teal-40:rgba(41,210,215,.42);
  --praxis-color-teal-50:#29D2D7;
  --praxis-color-teal-60:#29D2D7;
  --praxis-color-teal-70:#5CE0E5;
  --praxis-color-teal-80:#7FEAEE;      /* ink on the cyan tints — 8.2:1 */

  /* Destructive + pink, same treatment. #ff8b93 matches the Clear-all
     override already established above. pink-10 was only ever reachable
     through an inline #FDE6F1 fallback, which flared white on dark. */
  --praxis-color-red-10:rgba(255,139,147,.16);
  --praxis-color-red-50:#ff8b93;
  --praxis-color-red-60:#ff8b93;
  --praxis-color-pink-10:rgba(255,122,184,.18);
  --praxis-color-pink-60:#FF7AB8;
}

/* ---- Fills that carry light ink -------------------------------------
   These use a scale token as a BACKGROUND, so the inversion above would
   have put near-white ink on a near-white fill. Each takes the dark-CTA
   pattern from praxis-core instead: bright cyan fill, dark ink. */
body[data-theme="dark"] .segmented__opt--active {
  background: var(--praxis-color-teal-60); color: var(--px-primary-fg, #08313a);
}
body[data-theme="dark"] .segmented__opt--active:hover {
  background: #5CE0E5; color: var(--px-primary-fg, #08313a);
}
body[data-theme="dark"] .filter-view-switch__opt.is-active,
body[data-theme="dark"] .filter-view-switch__opt.is-active:hover {
  color: var(--px-primary-fg, #08313a);
}
/* Checkbox ticks: white-on-cyan is ~1.9:1. The faces fill with teal-60,
   so the glyph takes the same dark ink the CTAs use. */
body[data-theme="dark"] .qfilter__check,
body[data-theme="dark"] .select-menu__check { color: var(--px-primary-fg, #08313a); }

/* AND/OR join pills read as one control, so both take a bright fill with
   dark ink — the hardcoded deep magenta on OR would otherwise pair a
   white-ink pill against a dark-ink one. */
body[data-theme="dark"] .cf-join .cf-join__and.is-active {
  background: var(--praxis-color-teal-60); color: var(--px-primary-fg, #08313a);
}
body[data-theme="dark"] .cf-join .cf-join__or.is-active {
  background: #FF7AB8; color: #3d0020;
}

/* Tooltip: neutral-90 was its fill, which the inversion turns near-white.
   Takes the page's own dark tooltip/snackbar slate. */
body[data-theme="dark"] .filter-type-btn::after {
  background: #2b3648; color: var(--praxis-color-text-primary, #e7ebf1);
}

/* ---- Hardcoded light panels ----------------------------------------- */
/* The depth-0 group is the builder's outermost card: white in light, so it
   reads as the panel surface rather than the recessed well its nested
   children sit in. Same intent in dark, one tier up from neutral-05. */
body[data-theme="dark"] .cf-group--d0 { background: var(--px-surface); }
body[data-theme="dark"] .custom-summary__expr { background: rgba(0,0,0,.22); }
body[data-theme="dark"] .custom-summary__count {
  background: rgba(0,0,0,.22); color: var(--praxis-color-teal-80);
}
/* Expression chips: the OR variant hardcodes a pale pink fill + deep
   magenta ink. Mirror the AND chip's tint-plus-bright-ink construction. */
body[data-theme="dark"] .cfx-bool--or {
  background: rgba(255,122,184,.18); color: #FF7AB8;
}
body[data-theme="dark"] .cf-remove:hover { color: #ff8b93; }

/* Custom-filter chips carry the cyan accent, but the bar-wide dark rule
   above (.filter-chips .chip) outranks .chip--custom and was flattening
   them to plain ink. Re-assert with the theme prefix so it wins. */
body[data-theme="dark"] .filter-chips .chip--custom { color: var(--praxis-color-teal-80); }

/* ---- Materials ------------------------------------------------------
   The modal's shadow, the footer's lip shadow and the scrim are all built
   from light-mode navy (rgba(32,42,53,…)), which is invisible over a dark
   page — the modal lost its separation from the content behind it. Take
   the Praxis dark overlay material, and deepen the scrim so the panel
   still reads as modal. */
body[data-theme="dark"] .filter-drawer { box-shadow: var(--px-overlay); }
body[data-theme="dark"] .drawer-scrim { background: rgba(4,8,16,.62); }
body[data-theme="dark"] .filter-drawer__foot {
  box-shadow: 0 -8px 16px rgba(0,0,0,.28), 0 -2px 4px rgba(0,0,0,.20);
}

/* Row hover used neutral-05, which inverts DARKER than the card it sits on
   — a hover that reads as a hole. Use the theme's own hover wash. */
body[data-theme="dark"] .qfilter__row:hover { background: var(--px-hover); }

/* .cf-input never declares a background — in light the UA default is white,
   which passes for the same field colour its sibling .filter-select paints
   with --px-surface-2. Under color-scheme:dark the UA default is a neutral
   grey instead, so the value inputs sat warm and light against the navy
   selects beside them. Name the surface rather than inherit it. */
body[data-theme="dark"] .cf-input { background: var(--px-surface-2); }

/* Search focus fell back to a near-white border after the inversion; the
   cyan focus border matches the other inputs in the panel. */
body[data-theme="dark"] .filter-search-input input:focus {
  border-color: var(--praxis-color-teal-50);
}
