@layer rb2 {
  /* ================================================================
     REBENCH UI — Design Tokens
     All selectors scoped under .rb2.
     ================================================================ */

  .rb2 {
    /* ── Brand ── */
    --rb-brand:        #FFAD05;
    --rb-brand-strong: #E89C00;
    --rb-brand-soft:   #FFF3D6;
    --rb-ink:          #22262A;
    --rb-ink-2:        #4A5056;
    --rb-ink-3:        #6E747A;
    --rb-cream:        #EFEBE2;

    /* ── Surfaces & borders ── */
    --rb-surface:      #FFFFFF;
    --rb-surface-2:    #F7F4EC;
    --rb-border:       #E3DFD4;
    --rb-border-2:     #C9C4B8;

    /* ── Semantic ── */
    --rb-success:      #15683E;  --rb-success-soft: #E3F4EA;
    --rb-warning:      #9A5B00;  --rb-warning-soft: #FFEFD2;
    --rb-danger:       #C92A2A;  --rb-danger-soft:  #FBE9E9;
    --rb-info:         #1D6FB8;  --rb-info-soft:    #E7F1FA;

    /* ── Focus ── */
    --rb-focus:        #22262A;

    /* ── Typography ── */
    --rb-font: 'Outfit', sans-serif;
    --rb-text-xs:  0.75rem;
    --rb-text-sm:  0.875rem;
    --rb-text-md:  1rem;
    --rb-text-lg:  1.125rem;
    --rb-text-xl:  1.375rem;
    --rb-text-2xl: 1.75rem;
    --rb-leading-tight: 1.25;
    --rb-leading:       1.5;

    /* ── Spacing (base 4px) ── */
    --rb-s1: 4px;  --rb-s2: 8px;  --rb-s3: 12px; --rb-s4: 16px;
    --rb-s5: 24px; --rb-s6: 32px; --rb-s7: 48px; --rb-s8: 64px;

    /* ── Radius ── */
    --rb-r-sm:   8px;
    --rb-r-md:   12px;
    --rb-r-lg:   16px;
    --rb-r-full: 999px;

    /* ── Shadows ── */
    --rb-shadow-sm: 0 1px 2px rgba(34,38,42,.06);
    --rb-shadow-md: 0 2px 8px rgba(34,38,42,.10);
    --rb-shadow-lg: 0 8px 28px rgba(34,38,42,.16);

    /* ── Motion ── */
    --rb-t-fast: 120ms;
    --rb-t-base: 180ms;
    --rb-t-slow: 280ms;
    --rb-ease: cubic-bezier(.2,.8,.4,1);

    /* ── Apply base styles ── */
    font-family: var(--rb-font);
    color: var(--rb-ink);
    line-height: var(--rb-leading);
    background: var(--rb-cream);
  }

  /* ── Radzen overrides inside .rb2 ──
     Radzen's default.css ships a COOL grey scale (--rz-base-* = #f6f7fa…#28363c)
     and a BLUE accent (--rz-secondary = #35a0d7). Both leak everywhere — borders,
     focus rings, checkboxes, switches, tabs, links, progress bars — and read as
     off-brand "grey/blue". We remap the whole Radzen palette onto our warm brand
     tokens here, in one place, so every Radzen component inherits the brand without
     per-component CSS. Text-colour usages (tab labels, links) are pinned to readable
     ink further below — yellow text on cream would be unreadable. */
  .rb2 {
    /* Primary = brand yellow, with DARK text/icon on it (CLAUDE.md §7). */
    --rz-primary: var(--rb-brand);
    --rz-primary-light: var(--rb-brand);
    --rz-primary-lighter: var(--rb-brand-soft);
    --rz-primary-dark: var(--rb-brand-strong);
    --rz-primary-darker: var(--rb-brand-strong);
    --rz-on-primary: var(--rb-ink);
    --rz-on-primary-light: var(--rb-ink);
    --rz-on-primary-lighter: var(--rb-ink);
    --rz-on-primary-dark: var(--rb-ink);
    --rz-on-primary-darker: var(--rb-ink);

    /* Secondary is Radzen's blue accent — it drives checkbox/switch "checked",
       the tab indicator, progress-bar fill and the focus outline. Point it at the
       brand so all those accents go yellow; on-secondary becomes dark ink so a
       checkmark/label on the yellow fill stays legible. */
    --rz-secondary: var(--rb-brand);
    --rz-secondary-light: var(--rb-brand-strong);
    --rz-secondary-lighter: rgba(255, 173, 5, .18);
    --rz-secondary-dark: var(--rb-brand-strong);
    --rz-secondary-darker: var(--rb-brand-strong);
    --rz-on-secondary: var(--rb-ink);
    --rz-on-secondary-light: var(--rb-ink);
    --rz-on-secondary-lighter: var(--rb-ink);
    --rz-on-secondary-dark: var(--rb-ink);
    --rz-on-secondary-darker: var(--rb-ink);

    /* Warm neutral scale — replaces the cool greys. Mapped onto the cream/ink
       tokens so borders, disabled states, base buttons, tab/grid surfaces all
       sit in the brand's warm family instead of clashing blue-grey. */
    --rz-base-50:  #FFFFFF;
    --rz-base-100: var(--rb-surface-2);
    --rz-base-200: #EAE5DA;
    --rz-base-300: var(--rb-border);
    --rz-base-400: var(--rb-border-2);
    --rz-base-500: #A39E92;
    --rz-base-600: var(--rb-ink-3);
    --rz-base-700: var(--rb-ink-2);
    --rz-base-800: #2E3338;
    --rz-base-900: var(--rb-ink);

    /* Base button / generic neutral surface — warm light, dark text. */
    --rz-base:         var(--rb-cream);
    --rz-base-light:   var(--rb-surface-2);
    --rz-base-lighter: var(--rb-surface-2);
    --rz-base-dark:    var(--rb-border);
    --rz-base-darker:  var(--rb-border-2);
    --rz-on-base:         var(--rb-ink);
    --rz-on-base-light:   var(--rb-ink);
    --rz-on-base-lighter: var(--rb-ink);

    /* Semantic colours — use our tokens (Radzen's are cooler/lighter). */
    --rz-danger:         var(--rb-danger);
    --rz-danger-light:   var(--rb-danger);
    --rz-danger-lighter: var(--rb-danger-soft);
    --rz-danger-dark:    #A81F1F;
    --rz-danger-darker:  #A81F1F;
    --rz-success:         var(--rb-success);
    --rz-success-light:   var(--rb-success);
    --rz-success-lighter: var(--rb-success-soft);
    --rz-success-dark:    #14653C;
    --rz-success-darker:  #14653C;
    --rz-info:         var(--rb-info);
    --rz-info-light:   var(--rb-info);
    --rz-info-lighter: var(--rb-info-soft);
    --rz-info-dark:    #155A95;
    --rz-info-darker:  #155A95;
    --rz-warning:         var(--rb-brand);
    --rz-warning-light:   var(--rb-brand);
    --rz-warning-lighter: var(--rb-brand-soft);
    --rz-warning-dark:    var(--rb-brand-strong);
    --rz-warning-darker:  var(--rb-brand-strong);

    /* Focus ring = ink (accessible on cream); inputs get a brand border on focus. */
    --rz-outline-color: var(--rb-focus);
    --rz-border-focus: var(--rz-border-width) solid var(--rb-brand);

    --rz-text-font-family: var(--rb-font);
    --rz-text-color: var(--rb-ink);
    --rz-border-color: var(--rb-border);
    --rz-border-radius: var(--rb-r-sm);

    /* Tab labels & links must stay readable — pin them to ink, not the (now yellow)
       secondary. The tab's selected indicator keeps the brand via --rz-secondary. */
    --rz-tabs-tab-color: var(--rb-ink-2);
    --rz-tabs-tab-hover-color: var(--rb-ink);
    --rz-tabs-tab-focus-color: var(--rb-ink);
    --rz-tabs-tab-selected-color: var(--rb-ink);
    --rz-tabs-tab-selected-top-border-color: var(--rb-brand);
    --rz-link-color: var(--rb-ink);
    --rz-link-hover-color: var(--rb-brand-strong);
  }

  /* ── Component-level variables (the important part) ──
     Radzen's default.css declares per-component vars like
     `--rz-checkbox-checked-background-color: var(--rz-secondary)` on :root. Custom
     properties are substituted at the element where they're DECLARED, so these were
     already computed against the :root defaults (blue secondary / cool-grey base) and
     inherit that frozen value downward. Redefining only --rz-secondary / --rz-base-*
     on .rb2 therefore does NOT reach them (verified: checkbox/switch/progress stayed
     blue, grid header stayed cool grey). We must restate each consumed component var
     here on .rb2 with literal brand/warm values so the cascade picks the nearer scope.
     Only the components actually used in the UI are covered. */
  .rb2 {
    /* Checkbox — brand fill, dark (legible) checkmark. */
    --rz-checkbox-checked-background-color: var(--rb-brand);
    --rz-checkbox-checked-color: var(--rb-ink);
    --rz-checkbox-checked-hover-background-color: var(--rb-brand-strong);
    --rz-checkbox-checked-disabled-background-color: var(--rb-brand-soft);

    /* Switch — brand track, dark knob when on. */
    --rz-switch-checked-background-color: var(--rb-brand);
    --rz-switch-checked-circle-background-color: var(--rb-ink);

    /* Progress bar fill. */
    --rz-progressbar-value-background-color: var(--rb-brand);

    /* DataGrid — warm header/stripes, brand hover/selection (cool greys → cream). */
    --rz-grid-header-background-color: var(--rb-surface-2);
    --rz-grid-header-sorted-background-color: #EAE5DA;
    --rz-grid-foot-background-color: var(--rb-surface-2);
    --rz-grid-frozen-cell-background-color: var(--rb-surface-2);
    --rz-grid-stripe-background-color: var(--rb-surface-2);
    --rz-grid-filter-background-color: var(--rb-surface-2);
    --rz-grid-filter-buttons-background-color: var(--rb-surface-2);
    --rz-grid-clear-filter-button-background-color: var(--rb-cream);
    --rz-grid-hover-background-color: rgba(255, 173, 5, .08);
    --rz-grid-selected-background-color: var(--rb-brand-soft);
    --rz-grid-selected-color: var(--rb-ink);
    --rz-grid-cell-focus-background-color: rgba(255, 173, 5, .16);
    --rz-grid-filter-focus-color: var(--rb-brand);
    --rz-grid-column-resizer-helper-background-color: var(--rb-brand);

    /* DropDown popup items. */
    --rz-dropdown-item-hover-background-color: var(--rb-brand-soft);
    --rz-dropdown-item-selected-background-color: var(--rb-brand-soft);
    --rz-dropdown-item-selected-hover-background-color: var(--rb-brand);

    /* Numeric spinner buttons. */
    --rz-numeric-button-background-color: var(--rb-brand);
    --rz-numeric-button-disabled-background-color: var(--rb-border);

    /* FormField (filled variant) — warm, not cool grey. */
    --rz-form-field-filled-background-color: var(--rb-surface-2);
    --rz-form-field-filled-hover-background-color: #EAE5DA;

    /* Inputs & borders — warm neutrals, brand on focus. */
    --rz-input-disabled-background-color: var(--rb-surface-2);
    --rz-input-border: var(--rz-border-width) solid var(--rb-border-2);
    --rz-input-hover-border: var(--rz-border-width) solid var(--rb-ink-3);
    --rz-input-focus-border: var(--rz-border-width) solid var(--rb-brand);
    /* The theme styles the BOTTOM edge of inputs through separate *-border-block-end
       vars, precomputed on :root from the blue secondary (--rz-outline-color). Without
       these restates a focused dropdown/textbox keeps a 1px blue bottom line while the
       other three sides are brand. */
    --rz-input-border-block-end: var(--rz-input-border);
    --rz-input-hover-border-block-end: var(--rz-input-hover-border);
    --rz-input-focus-border-block-end: var(--rz-input-focus-border);
    --rz-border-normal: var(--rz-border-width) solid var(--rb-border-2);
    --rz-border-hover: var(--rz-border-width) solid var(--rb-ink-3);
    --rz-border-disabled: var(--rz-border-width) solid var(--rb-border);

    /* Focus outline — ink ring (accessible on cream), restated so it recomputes. */
    --rz-outline-focus: var(--rz-outline-width) solid var(--rb-focus);

    /* Layout surface — the main content/body background. Radzen sets
       `--rz-body-background-color: var(--rz-base-100)` (cool grey #f6f7fa) on :root and
       paints `.rz-body`/`.rz-layout` with it in an UNLAYERED rule, so a competing
       `.rb2 .rz-body { background }` in @layer loses — unlayered NORMAL beats layered normal.
       (Not "the only way": Radzen's declaration carries no !important, so a layered
       `!important` would win too. Overriding the variable the unlayered rule reads is simply
       the cleaner one, and it keeps `--rz-layout-background-color` in step.) */
    --rz-body-background-color: var(--rb-cream);
    --rz-layout-background-color: var(--rb-cream);

    /* Scrollbar thumb — was the blue secondary; make it a subtle warm neutral. */
    --rz-scrollbar-color: var(--rb-border-2);
  }

  /* ── Neutralize Bootstrap leaks (targeted, not all:revert — preserves Radzen styling) ── */
  .rb2 .form-control {
    border: 1px solid var(--rb-border-2);
    border-radius: var(--rb-r-sm);
    padding: var(--rb-s2) var(--rb-s3);
    font-family: var(--rb-font);
    font-size: var(--rb-text-md);
    color: var(--rb-ink);
    background-color: var(--rb-surface);
    box-shadow: none;
  }
  .rb2 .btn {
    border-radius: var(--rb-r-sm);
    font-family: var(--rb-font);
    font-weight: 500;
    box-shadow: none;
  }

  /* ── Buttons: one reusable rounding for every Radzen button in the UI, matching
     the toolbar buttons (--rb-r-md) and the rest of the surfaces. Radzen's default.css
     radius is unlayered, so !important in @layer is needed to apply it. */
  .rb2 .rz-button {
    border-radius: var(--rb-r-md) !important;
  }

  /* ── Brand CTA buttons: filled primary = YELLOW background with DARK text/icon
     (mockup + CLAUDE.md §7: "yellow #FFAD05 … always with dark text"). Forced
     explicitly rather than via the --rz-primary custom property, because Radzen's
     default.css is unlayered and reliably beats @layer for both the colour vars and
     the button rules; an !important here in @layer wins. Icon uses currentColor. */
  /* Both Primary and Warning filled buttons are the brand CTA in this app (the Add
     button uses ButtonStyle.Warning), so they share the yellow look. */
  .rb2 .rz-button.rz-primary.rz-variant-filled,
  .rb2 .rz-button.rz-primary.rz-variant-filled:focus,
  .rb2 .rz-button.rz-warning.rz-variant-filled,
  .rb2 .rz-button.rz-warning.rz-variant-filled:focus {
    background-color: var(--rb-brand) !important;
    border-color: var(--rb-brand) !important;
    color: var(--rb-ink) !important;
  }

  .rb2 .rz-button.rz-primary.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rz-button.rz-primary.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active,
  .rb2 .rz-button.rz-warning.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rz-button.rz-warning.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active {
    background-color: var(--rb-brand-strong) !important;
    border-color: var(--rb-brand-strong) !important;
    color: var(--rb-ink) !important;
  }

  /* Other primary/warning variants (text/flat/outlined) keep the dark brand ink for text. */
  .rb2 .rz-button.rz-primary,
  .rb2 .rz-button.rz-warning {
    color: var(--rb-ink) !important;
  }

  /* ── Condition button-group selected state (QrUnitEditForm + QrSeriesDefaults) ──
     The selected condition uses ButtonStyle.Primary/Warning, both of which render as
     the same brand yellow as the unselected Light buttons above — so fill alone gives
     no affordance. Mark the selected button (aria-pressed="true") with a dark offset
     ring + a leading check + bold text. #22262A on cream/yellow both clear WCAG AA.
     Box-shadow (not border width) avoids any layout shift between states. */
  .rb2 .qr-edit__conditions .rz-button.rz-variant-filled[aria-pressed="true"],
  .rb2 .qr-series__conditions .rz-button.rz-variant-filled[aria-pressed="true"] {
    box-shadow:
      inset 0 0 0 2px var(--rb-ink),
      0 0 0 2px var(--rb-cream),
      0 0 0 3px var(--rb-ink) !important;
    font-weight: 700 !important;
  }

  .rb2 .qr-edit__conditions .rz-button.rz-variant-filled[aria-pressed="true"]::before,
  .rb2 .qr-series__conditions .rz-button.rz-variant-filled[aria-pressed="true"]::before {
    content: "\2713"; /* ✓ — independent of the Material icon font */
    margin-right: 0.3rem;
    font-weight: 700;
    line-height: 1;
  }

  /* ── ButtonStyle.Light buttons: brand yellow with dark text, same as the primary CTA.
     Radzen's default rz-light is a generic grey that clashes with the palette; in this
     app every filled button is the brand yellow. One rule, applies everywhere. */
  .rb2 .rz-button.rz-light.rz-variant-filled,
  .rb2 .rz-button.rz-light.rz-variant-filled:focus {
    background-color: var(--rb-brand) !important;
    border: 1px solid var(--rb-brand) !important;
    color: var(--rb-ink) !important;
  }

  .rb2 .rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active {
    background-color: var(--rb-brand-strong) !important;
    border-color: var(--rb-brand-strong) !important;
    color: var(--rb-ink) !important;
  }

  /* ── Selection bar (RbSelectionBar) action pills ──────────────────────────
     The bar is a dark surface, so its Light buttons must be neutral grey pills
     (mockup) — NOT the global brand-yellow above. The .rb-selbar / .rb-selbar-sheet
     ancestor lifts these over the generic .rb2 .rz-button.rz-light.rz-variant-filled
     rule (a layered !important, which a scoped ::deep !important cannot beat). */
  .rb2 .rb-selbar .rz-button.rz-light.rz-variant-filled,
  .rb2 .rb-selbar .rz-button.rz-light.rz-variant-filled:focus,
  .rb2 .rb-selbar-sheet .rz-button.rz-light.rz-variant-filled,
  .rb2 .rb-selbar-sheet .rz-button.rz-light.rz-variant-filled:focus {
    background-color: #33383d !important;
    border: 1px solid #33383d !important;
    color: #EFEBE2 !important;
  }

  .rb2 .rb-selbar .rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rb-selbar .rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active,
  .rb2 .rb-selbar-sheet .rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rb-selbar-sheet .rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active {
    background-color: #3d4248 !important;
    border-color: #3d4248 !important;
    color: #EFEBE2 !important;
  }

  /* ── Lightweight icon controls (filter-panel collapse toggle + table-row "⋮") ──
     These sit directly on a page surface, not as brand CTAs — they must blend with
     their background (cream filter column / white grid cell), NOT read as the global
     brand-yellow Light button above. Transparent fill inherits whatever is behind them
     so only the dark icon shows; a faint surface tint gives hover/active feedback.
     Same layered-!important lift over the generic .rz-light rule as the selection bar. */
  .rb2 .rb-filter-sidebar__collapse-btn.rz-button.rz-light.rz-variant-filled,
  .rb2 .rb-filter-sidebar__collapse-btn.rz-button.rz-light.rz-variant-filled:focus,
  .rb2 .rb-actions-menu__trigger.rz-button.rz-light.rz-variant-filled,
  .rb2 .rb-actions-menu__trigger.rz-button.rz-light.rz-variant-filled:focus {
    background-color: transparent !important;
    border-color: transparent !important;
    color: var(--rb-ink) !important;
  }

  .rb2 .rb-filter-sidebar__collapse-btn.rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rb-filter-sidebar__collapse-btn.rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active,
  .rb2 .rb-actions-menu__trigger.rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):hover,
  .rb2 .rb-actions-menu__trigger.rz-button.rz-light.rz-variant-filled:not(:disabled):not(.rz-state-disabled):active {
    background-color: var(--rb-surface-2) !important;
    border-color: var(--rb-border) !important;
    color: var(--rb-ink) !important;
  }

  /* ── Header ── */
  .rb2 .rz-header {
    background: var(--rb-surface);
    border-bottom: 1px solid var(--rb-border);
    box-shadow: none;
  }

  /* ── Sidebar nav item overrides (light-theme defaults) ── */
  /* Dark sidebar (.rb2-sidebar) overrides these via MainLayout.razor.css with !important */
  .rb2 .rz-panel-menu .rz-navigation-item-wrapper {
    border-radius: 0;
  }

  .rb2 .rz-panel-menu .rz-navigation-item-wrapper:hover {
    background: var(--rb-surface-2);
  }

  .rb2 .rz-panel-menu .rz-navigation-item-text {
    font-size: var(--rb-text-sm);
    color: var(--rb-ink);
  }

  .rb2 .rz-panel-menu .rz-navigation-item-icon {
    color: var(--rb-ink-2);
    font-size: 20px;
  }

  /* ── Body background ── */
  .rb2 .rz-body {
    background: var(--rb-cream);
  }

  @media (prefers-reduced-motion: reduce) {
    .rb2 * {
      transition-duration: 1ms !important;
      animation-duration: 1ms !important;
    }
  }

  /* ── Radzen dropdown popups ──
     app.css forces every Radzen popup to `min-width: 300px
     !important`. That popup is appended to <body> (outside .rb2), so no scoped
     rule can reach it — and 300px makes the grid page-size dropdown (10/25/50/
     100) overflow the viewport and stretch the page. A layered !important
     overrides the unlayered !important; the panel then sizes to its trigger. */
  .rz-dropdown-panel.rz-popup {
    min-width: auto !important;
    max-width: 90vw;
  }

  /* The .rb2 --rz-dropdown-item-* / --rz-scrollbar-color remaps above never reach these
     panels: Radzen portals them to <body>, OUTSIDE .rb2, so the highlighted item shows
     the default-theme blue (#3193c6, white text) and the scrollbar the blue secondary.
     Re-declare the item-state vars directly on the panel (theme declares them on :root,
     so an element-level declaration wins — no !important). --rb-* tokens are also scoped
     to .rb2, hence the literals (kept in sync with the token block by the comments). */
  .rz-dropdown-panel.rz-popup,
  .rz-multiselect-panel.rz-popup,
  .rz-lookup-panel.rz-popup,
  .rz-autocomplete-panel.rz-popup {
    --rz-dropdown-item-hover-background-color: #FFF3D6;          /* --rb-brand-soft */
    --rz-dropdown-item-hover-color: #22262A;                     /* --rb-ink */
    --rz-dropdown-item-selected-background-color: #FFF3D6;       /* --rb-brand-soft */
    --rz-dropdown-item-selected-color: #22262A;                  /* --rb-ink */
    --rz-dropdown-item-selected-hover-background-color: #FFAD05; /* --rb-brand */
    --rz-dropdown-item-selected-hover-color: #22262A;            /* --rb-ink */
    --rz-scrollbar-color: #C9C4B8;                               /* --rb-border-2 */

    /* Match the RbSearchBar dropdown card look (border/shadow are var-driven in the
       theme's panel rule, so restating the vars is enough; radius is not — the theme
       reads --rz-input-border-radius, which would also round inputs INSIDE the panel,
       so the property is set directly and needs !important against the unlayered rule). */
    --rz-dropdown-panel-border: 1.5px solid #E3DFD4;             /* --rb-border */
    --rz-dropdown-panel-shadow: 0 8px 28px rgba(34, 38, 42, .16); /* --rb-shadow-lg */
    border-radius: 12px !important;                              /* --rb-r-md */
  }

  /* Clip item hover/selection backgrounds to the rounded corners. Scrolling lives in
     the inner items wrapper, so hiding overflow here is safe for these two panels —
     NOT for .rz-autocomplete-panel, which scrolls on the panel element itself. */
  .rz-dropdown-panel.rz-popup,
  .rz-multiselect-panel.rz-popup {
    overflow: hidden;
  }

  /* ── Popup height on a phone ──
     Radzen positions its portalled popups in JS and clamps them against window.innerHeight
     (Radzen.Blazor.js: `if (popupRect.bottom > window.innerHeight) popup.style.top = ...`).
     In the edge-to-edge webview that is the WHOLE webview, so a dropdown opened low on the
     page — the grid page-size picker sits at the bottom of every list page — gets pinned with
     its last options over the bottom nav and inside the gesture strip. Radzen never asks about
     app chrome, so cap the height instead: what doesn't fit scrolls, and the popup stays in the
     part of the screen the user can actually touch.

     The cap goes on the ITEMS WRAPPER for the two panels made `overflow: hidden` above (a cap
     on the panel itself would clip them instead of scrolling) and on the panel for the rest.
     These are outside .rb2 (portalled to <body>), so only :root-scoped vars are available —
     hence the literal nav height rather than --rb2-bottomnav-h. */
  .rz-dropdown-items-wrapper,
  .rz-multiselect-items-wrapper,
  .rz-autocomplete-panel.rz-popup,
  .rz-lookup-panel.rz-popup {
    max-height: min(50dvh,
                    calc(100dvh - 68px - var(--rb-safe-top, 0px) - var(--rb-safe-bottom, 0px))) !important;
    overflow-y: auto;
  }

  /* ── DataGrids: mockup .rb-table look ──
     Only a horizontal line per row, no vertical column dividers. Radzen 5.x
     DataGrid renders a `.rz-grid-table` whose cells get vertical borders from
     `.rz-grid-gridlines-both` in the unlayered default.css; a layered
     !important beats it. Applies to every grid (reusable, one place). */
  .rb2 .rz-grid-table th,
  .rb2 .rz-grid-table td {
    border-left-width: 0 !important;
    border-right-width: 0 !important;
  }

  /* ── DataGrids: one consistent rounded card for every grid ──
     Done globally (not per-page) because per-page scoped `::deep .x-grid`
     rules can't reach the grid's own root element, so items/locations stayed
     boxy. Border + big radius + clip so header/rows/pager follow the rounded
     shape. */
  .rb2 .rz-data-grid {
    background: var(--rb-surface) !important;
    border: 1px solid var(--rb-border) !important;
    border-radius: var(--rb-r-lg) !important;
    box-shadow: var(--rb-shadow-sm);
    overflow: hidden !important;
  }

  /* When a column is resized wider than the grid, scroll horizontally instead
     of clipping (the grid root keeps overflow:hidden for its rounded corners). */
  .rb2 .rz-data-grid .rz-data-grid-data {
    box-shadow: none;
    overflow-x: auto;
  }

  /* Always stretch the table to fill the available width (column resize puts
     Radzen in table-layout:fixed, which otherwise leaves whitespace on the
     right). Wider-than-container resizes overflow into the scroll above. */
  .rb2 .rz-data-grid .rz-grid-table {
    min-width: 100%;
  }

  /* Items grid: the "manage labels" icon button is hidden until the row is
     hovered (or focused). Radzen rows/cells carry no Blazor scoped attribute,
     so this row-level reveal can't live in the component's scoped CSS. Opacity
     is fully controlled here (inside @layer) so it doesn't lose to other rules. */
  .rb2 .rb-items-grid .items-grid__label-btn {
    opacity: 0;
    transition: opacity var(--rb-t-fast) var(--rb-ease);
  }

  .rb2 .rb-items-grid tr:hover .items-grid__label-btn,
  .rb2 .rb-items-grid .items-grid__label-btn:focus-visible {
    opacity: 1;
  }

  /* Touch devices can't hover — keep the action discoverable. */
  @media (hover: none) {
    .rb2 .rb-items-grid .items-grid__label-btn {
      opacity: 1;
    }
  }

  /* Global-search focus: highlight the matched row. Static (not a keyframe) because
     RowRender re-applies row classes on every grid re-render (paging/sort/expand),
     which would re-trigger an animation and flicker. Radzen rows carry no scoped
     attribute, so this lives here in @layer, not the component's scoped CSS. */
  .rb2 .rb-items-grid tr.rb-items-grid__row--focused > td {
    background: rgba(255, 173, 5, 0.16);
  }

  /* Accent bar only on the row's FIRST cell — otherwise the inset shadow paints a
     3px orange bar on every <td>, which reads as orange lines between all columns
     for the ~3s the search-focus highlight is active. One bar = one row accent. */
  .rb2 .rb-items-grid tr.rb-items-grid__row--focused > td:first-child {
    box-shadow: inset 3px 0 0 var(--rb-accent, #FFAD05);
  }

  /* The grid's built-in Radzen pager is hidden — the shared <RbPager> below the grid
     drives paging. Radzen 5.9.5 renders it as .rz-pager (not .rz-paginator), and it
     carries no scoped attribute, so (like the rules above) this hide lives here in
     @layer, not the component's scoped CSS. */
  .rb2 .rb-items-grid .rz-pager,
  .rb2 .rb-units-grid .rz-pager,
  .rb2 .projects-grid .rz-pager,
  .rb2 .locations-grid .rz-pager,
  .rb2 .templates-grid .rz-pager {
    /* !important: Radzen's default.css is unlayered and would otherwise beat this
       @layer rule. We're hard-hiding the built-in pager (RbPager replaces it). */
    display: none !important;
  }

  /* ── Narrow viewports: the list grids fit the phone ─────────────────────────────
     Below 768px the grid takes back the two 16px gutters per side, and each column kind gets a
     minimum width. While the picked columns fit at their floors they all share the screen; past
     that the table scrolls sideways with the title pinned. Counts come from
     RbGridColumnCss.NarrowStyle. Excludes .rb-units-grid and .warehouse-detail-grid. */
  @media (max-width: 768px) {
    .rb2 .rb-items-grid,
    .rb2 .projects-grid,
    .rb2 .locations-grid,
    .rb2 .templates-grid {
      /* Two gutters PER SIDE, each var(--rb-s4) — see (1) above for where each comes from.
         So 2 × 16px back on each edge, 4 × 16px of width in total. */
      width: calc(100% + 4 * var(--rb-s4));
      margin-inline: calc(-2 * var(--rb-s4));
      /* !important mirrors the base .rz-data-grid rule above, which sets both with it. */
      border-inline: 0 !important;
      border-radius: 0 !important;
    }

    /* cqi = the scroller's width. A cell's % resolves against the table, which is what grows. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data {
      container-type: inline-size;
    }

    /* Floors, measured in Outfit at the smallest type; each grows by `grow` px per px of
       --rb-grid-u. The words they keep whole are pinned by
       GridNarrowFitTests.Floors_StayWideEnoughForTheWordsTheyWereMeasuredFor. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table {
      --rb-grid-min-chrome: 22px;
      --rb-grid-grow-chrome: 1;
      --rb-grid-min-actions: 24px;
      --rb-grid-grow-actions: 2;
      --rb-grid-min-compact: 46px;
      --rb-grid-grow-compact: 4.3;
      --rb-grid-min-long: 14.5px;
      --rb-grid-grow-long: 1.15;
      --rb-grid-min-badge: 54.5px;
      --rb-grid-grow-badge: 6;
      /* The title grows fastest: it is the row's identity. */
      --rb-grid-min-title: 53px;
      --rb-grid-grow-title: 21;
      --rb-grid-min-text: 49.2px;
      --rb-grid-grow-text: 6.2;
      --rb-grid-inset: 6px;
      /* Extra lead of a count or chip header right after a text header (see n-gap). */
      --rb-grid-lead-extra: 2px;
      /* Widths once the table scrolls (unitless px). At least what a default set gets at 390px,
         so adding a column does not narrow the others. */
      --rb-grid-wide-compact: 66;
      --rb-grid-wide-badge: 78;
      --rb-grid-wide-title: 136;
      --rb-grid-wide-text: 100;
    }

    /* Width model, switched by --rb-grid-scrolls (0px or 1px).
       FITS: type and floors grow together (u up to 2px); spare goes to the title and text columns.
       SCROLLS: full-size type, every column at its wide width, the title pinned.
       Child combinators keep the units grid nested in an expanded row out. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table {
      --rb-grid-floors: calc(var(--rb-grid-n-chrome, 0) * var(--rb-grid-min-chrome)
                             + var(--rb-grid-n-actions, 0) * var(--rb-grid-min-actions)
                             + var(--rb-grid-n-inset, 0) * var(--rb-grid-inset)
                             + var(--rb-grid-n-gap, 0) * var(--rb-grid-lead-extra)
                             + var(--rb-grid-n-compact, 0) * var(--rb-grid-min-compact)
                             + var(--rb-grid-n-long, 0) * var(--rb-grid-min-long)
                             + var(--rb-grid-n-badge, 0) * var(--rb-grid-min-badge)
                             + var(--rb-grid-n-title, 0) * var(--rb-grid-min-title)
                             + var(--rb-grid-n-text, 0) * var(--rb-grid-min-text));
      --rb-grid-growth: calc(var(--rb-grid-n-chrome, 0) * var(--rb-grid-grow-chrome)
                             + var(--rb-grid-n-actions, 0) * var(--rb-grid-grow-actions)
                             + var(--rb-grid-n-compact, 0) * var(--rb-grid-grow-compact)
                             + var(--rb-grid-n-long, 0) * var(--rb-grid-grow-long)
                             + var(--rb-grid-n-badge, 0) * var(--rb-grid-grow-badge)
                             + var(--rb-grid-n-title, 0) * var(--rb-grid-grow-title)
                             + var(--rb-grid-n-text, 0) * var(--rb-grid-grow-text));
      --rb-grid-scrolls: clamp(0px, (var(--rb-grid-floors) - 100cqi) * 100, 1px);
      --rb-grid-u: max(clamp(0px, (100cqi - var(--rb-grid-floors)) / var(--rb-grid-growth), 2px),
                       var(--rb-grid-scrolls) * 2);
      --rb-grid-spare: max(0px, 100cqi - var(--rb-grid-floors) - var(--rb-grid-growth) * var(--rb-grid-u));
      --rb-grid-w-chrome: calc(var(--rb-grid-min-chrome) + var(--rb-grid-grow-chrome) * var(--rb-grid-u));
      --rb-grid-w-actions: calc(var(--rb-grid-min-actions) + var(--rb-grid-grow-actions) * var(--rb-grid-u));
      --rb-grid-at-compact: max(var(--rb-grid-min-compact) + var(--rb-grid-grow-compact) * var(--rb-grid-u),
                                var(--rb-grid-scrolls) * var(--rb-grid-wide-compact));
      --rb-grid-at-long: max(var(--rb-grid-min-compact) + var(--rb-grid-min-long)
                             + (var(--rb-grid-grow-compact) + var(--rb-grid-grow-long)) * var(--rb-grid-u),
                             var(--rb-grid-scrolls) * var(--rb-grid-wide-compact));
      --rb-grid-at-badge: max(var(--rb-grid-min-badge) + var(--rb-grid-grow-badge) * var(--rb-grid-u),
                              var(--rb-grid-scrolls) * var(--rb-grid-wide-badge));
      --rb-grid-at-title: max(var(--rb-grid-min-title) + var(--rb-grid-grow-title) * var(--rb-grid-u),
                              var(--rb-grid-scrolls) * var(--rb-grid-wide-title));
      --rb-grid-at-text: max(var(--rb-grid-min-text) + var(--rb-grid-grow-text) * var(--rb-grid-u),
                             var(--rb-grid-scrolls) * var(--rb-grid-wide-text));
      /* With no checkbox in front, or once it has scrolled away, the title would touch the edge. */
      --rb-grid-title-inset: max(var(--rb-grid-n-inset, 0) * var(--rb-grid-inset),
                                 min(var(--rb-grid-inset), var(--rb-grid-scrolls) * 1000));
      /* Type and paddings grow with u; at u = 2px they are the desktop-like 12/11px. */
      --rb-grid-type-cell: calc(10px + var(--rb-grid-u));
      --rb-grid-type-head: calc(9.5px + 0.75 * var(--rb-grid-u));
      --rb-grid-pad: calc(1px + 0.75 * var(--rb-grid-u));
      --rb-grid-pad-tight: calc(1px + 0.5 * var(--rb-grid-u));
      /* After a count header, so "Jednostki" and "Stan" do not read as one phrase. */
      --rb-grid-head-gap: calc(3px + 0.5 * var(--rb-grid-u));
      /* A 20px checkbox or thumbnail, centred. */
      --rb-grid-pad-chrome: calc((var(--rb-grid-w-chrome) - 20px) / 2);
      width: 100% !important;
      min-width: calc(var(--rb-grid-n-chrome, 0) * var(--rb-grid-w-chrome)
                      + var(--rb-grid-n-gap, 0) * var(--rb-grid-lead-extra)
                      + var(--rb-grid-n-actions, 0) * var(--rb-grid-w-actions)
                      + (var(--rb-grid-n-compact, 0) - var(--rb-grid-n-long, 0)) * var(--rb-grid-at-compact)
                      + var(--rb-grid-n-long, 0) * var(--rb-grid-at-long)
                      + var(--rb-grid-n-badge, 0) * var(--rb-grid-at-badge)
                      + var(--rb-grid-n-title, 0) * (var(--rb-grid-at-title) + var(--rb-grid-title-inset))
                      + var(--rb-grid-n-text, 0) * var(--rb-grid-at-text)) !important;
      table-layout: fixed !important;
    }

    /* Radzen writes Width/MinWidth (and resize widths) inline; only !important drops them. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > colgroup > col,
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th,
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td {
      width: auto !important;
      min-width: 0 !important;
      padding-inline: var(--rb-grid-pad) !important;
      white-space: normal !important;
    }

    /* Smaller type fits a whole word per column. Declared on the cells, not the table: the tokens
       inherit, and the units grid nested in an expanded row keeps the theme's. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th {
      /* Uppercase headers need ~30% more width than mixed case. */
      --rz-grid-header-text-transform: none;
      --rz-grid-header-font-size: var(--rb-grid-type-head);
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td {
      --rz-grid-cell-font-size: var(--rb-grid-type-cell);
    }


    /* Wrap on spaces only; a word wider than its column ends in an ellipsis, not a mid-word break. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th :is(.rz-column-title, .rz-column-title-content),
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td .rz-cell-data {
      padding-inline: 0 !important;
      white-space: normal !important;
      /* !important: Radzen's unlayered `.rz-cell-data:has(.rz-button) { overflow: visible }` beats
         a layered rule whatever its specificity, and the Items name cell holds a button. */
      overflow: hidden !important;
      text-overflow: ellipsis !important;
    }

    /* A plain text value stops at three lines so one long address cannot make a row fill the
       screen; the full value is on the detail page. The title, chips and counts are not clamped. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td:not(.rb-grid-col-title, .rb-grid-col-select, .rb-grid-col-media, .rb-grid-col-actions, .rb-grid-col-badge, .rb-grid-col-compact) > .rz-cell-data {
      display: -webkit-box !important;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 3;
    }

    /* The unsorted sort glyph and the resize handle cost a narrow header its text, and a
       resize is overridden here anyway. Sorted icons keep `rzi-sort` too, hence the :not()s. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th .rz-sortable-column-icon.rzi-sort:not(.rzi-sort-asc):not(.rzi-sort-desc),
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th .rz-column-resizer {
      display: none !important;
    }

    /* Fixed layout sizes columns from the header row. Text columns stay auto and split the rest. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th:is(.rb-grid-col-select, .rb-grid-col-media) {
      width: var(--rb-grid-w-chrome) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-actions {
      width: var(--rb-grid-w-actions) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-compact {
      width: calc(var(--rb-grid-at-compact) + var(--rb-grid-lead, 0px)) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-compact.rb-grid-col-long {
      width: calc(var(--rb-grid-at-long) + var(--rb-grid-lead, 0px)) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-badge {
      width: calc(var(--rb-grid-at-badge) + var(--rb-grid-lead, 0px)) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-title {
      width: calc(var(--rb-grid-at-title) + var(--rb-grid-title-inset)
                  + var(--rb-grid-spare) * 2 / (2 + var(--rb-grid-n-text, 0))) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-title,
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-title {
      padding-inline-start: calc(var(--rb-grid-pad) + var(--rb-grid-title-inset)) !important;
    }

    /* Actions, counts and chips spend less on gutters. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th:is(.rb-grid-col-actions, .rb-grid-col-compact, .rb-grid-col-badge),
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td:is(.rb-grid-col-compact, .rb-grid-col-badge) {
      padding-inline: var(--rb-grid-pad-tight) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-compact,
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-compact {
      padding-inline-end: var(--rb-grid-head-gap) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th:is(.rb-grid-col-select, .rb-grid-col-media),
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td:is(.rb-grid-col-select, .rb-grid-col-media) {
      padding-inline: var(--rb-grid-pad-chrome) !important;
    }

    /* A count or chip header right after a text header gets more lead, so the two labels do not
       read as one phrase. The page counts these (n-gap) into the floors. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th:not(.rb-grid-col-select, .rb-grid-col-media, .rb-grid-col-title, .rb-grid-col-compact, .rb-grid-col-badge, .rb-grid-col-actions) + th:is(.rb-grid-col-compact, .rb-grid-col-badge),
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td:not(.rb-grid-col-select, .rb-grid-col-media, .rb-grid-col-title, .rb-grid-col-compact, .rb-grid-col-badge, .rb-grid-col-actions) + td:is(.rb-grid-col-compact, .rb-grid-col-badge) {
      --rb-grid-lead: var(--rb-grid-lead-extra);
      padding-inline-start: calc(var(--rb-grid-pad-tight) + var(--rb-grid-lead)) !important;
    }

    /* The kebab keeps a 24px target (WCAG 2.5.8), so its cell gives up the gutters instead.
       !important beats the scoped 36px min-width. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-actions {
      padding-inline: 0 !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-actions .rb-actions-menu__trigger {
      min-width: 24px !important;
      padding-inline: 0 !important;
    }

    /* "Zdjęcie" / "Akcje" do not fit and the column says it anyway; font-size keeps them for screen readers. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th:is(.rb-grid-col-media, .rb-grid-col-actions) .rz-column-title-content {
      font-size: 0;
    }

    /* A count never wraps: 240 broken over two lines reads as 24. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-compact,
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-compact .rz-cell-data {
      white-space: nowrap !important;
      font-variant-numeric: tabular-nums;
    }

    /* Direct children only: the thumbnail and the fixed 48px no-photo placeholder, which both have
       to shrink into a ~24px column. As a descendant selector this also hit Radzen's cell wrapper. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-media > .rz-cell-data > :is(img, div) {
      max-width: 100%;
      height: auto !important;
      aspect-ratio: 1;
    }

    /* A chip wraps between words ("Bardzo dobry") and ends a word too long for it in an ellipsis.
       text-overflow needs a non-flex box, and a two-line pill would clip its first letter on the
       curve. !important beats the unlayered scoped rule. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td .rb-status-chip {
      display: inline-block !important;
      max-width: 100%;
      padding-inline: var(--rb-grid-pad-tight) !important;
      border-radius: var(--rb-r-sm) !important;
      font-size: var(--rb-grid-type-head) !important;
      white-space: normal !important;
      overflow: hidden;
      text-overflow: ellipsis;
      vertical-align: middle;
    }

    /* PINNED TITLE, for when the floors overflow; inert otherwise. Radzen already pins every th
       to the top, this adds the left edge. The header must clear the body cells and its sibling
       ths; !important beats Radzen's unlayered `thead th { z-index: 1 }`. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-title {
      position: sticky;
      inset-inline-start: 0;
      z-index: 3 !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-title {
      position: sticky;
      inset-inline-start: 0;
      z-index: 1;
    }

    /* A pinned cell must be opaque, and Radzen's hover and focus tints are not, so they are
       composited over the opaque base instead. */
    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid).rz-selectable > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row:hover:not(.rz-state-highlight) > td.rb-grid-col-title {
      background: linear-gradient(var(--rz-grid-hover-background-color), var(--rz-grid-hover-background-color)) var(--rz-grid-background-color, #fff) !important;
    }

    .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid).rz-selectable > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row.rz-state-focused:not(:hover, .rz-state-highlight) > td.rb-grid-col-title {
      background: linear-gradient(var(--rz-grid-cell-focus-background-color), var(--rz-grid-cell-focus-background-color)) var(--rz-grid-background-color, #fff) !important;
    }

    /* The seam shows once the table has scrolled; without scroll timelines there is none. */
    @supports (animation-timeline: scroll()) {
      .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > thead > tr > th.rb-grid-col-title,
      .rb2 :is(.rb-items-grid, .projects-grid, .locations-grid, .templates-grid) > .rz-data-grid-data > .rz-grid-table > tbody > tr.rz-data-row > td.rb-grid-col-title {
        animation: rb-grid-pinned-seam linear both;
        animation-timeline: scroll(nearest inline);
        animation-range: 0 8px;
      }
    }

    @keyframes rb-grid-pinned-seam {
      from { box-shadow: none; }
      to { box-shadow: 3px 0 4px -3px rgba(34, 38, 42, 0.18); }
    }
  }

  /* Units grid (nested under each expanded item): same row-level treatments as the
     items grid. Radzen rows carry no scoped attribute, so these live here, not in
     UnitsList's scoped CSS. */
  .rb2 .rb-units-grid .units-list__label-btn {
    opacity: 0;
    transition: opacity var(--rb-t-fast) var(--rb-ease);
  }

  .rb2 .rb-units-grid tr:hover .units-list__label-btn,
  .rb2 .rb-units-grid .units-list__label-btn:focus-visible {
    opacity: 1;
  }

  @media (hover: none) {
    .rb2 .rb-units-grid .units-list__label-btn {
      opacity: 1;
    }
  }

  /* Selected unit row — class applied via RowRender. */
  .rb2 .rb-units-grid tr.units-list__row--selected > td {
    background: rgba(255, 173, 5, 0.12);
  }

  /* Visually-hidden scroll/test marker for the focused row. */
  .rb2 .rb-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* ── Shared form primitives ──
     These must be global (not scoped to one component): the create forms
     (ItemForm/ProjectForm/LocationForm) are extracted, route-free components
     hosted both as pages and inside the create drawer, so a per-component
     scoped rule would only reach one host. The required-field marker and
     validation text live here so every form renders the same red asterisk
     and red error text. */
  .rb2 .form-label--required::after {
    content: " *";
    color: var(--rb-danger);
    font-weight: 600;
  }

  .rb2 .validation-message,
  .rb2 .rb-field-error {
    display: block;
    margin-top: var(--rb-s1);
    font-size: var(--rb-text-xs);
    color: var(--rb-danger);
  }

  /* ── Form layout primitives ──
     Shared by ItemForm, ProjectForm and LocationForm so the three create forms line
     up identically whether they render on their own page or inside the create drawer.
     Global (not scoped) because each form is an extracted, route-free component with
     two hosts — a per-component scoped rule would only reach one of them. */
  .rb2 .rb-form {
    display: flex;
    flex-direction: column;
    gap: var(--rb-s4);
  }

  .rb2 .form-field {
    display: flex;
    flex-direction: column;
    gap: var(--rb-s1);
  }

  .rb2 .form-field .rz-textbox,
  .rb2 .form-field .rz-textarea,
  .rb2 .form-field .rz-dropdown,
  .rb2 .form-field .rz-numeric {
    width: 100%;
  }

  /* A control paired with an inline action button (e.g. project picker + "create new"):
     the control fills the row and truncates, the button keeps its natural width. */
  .rb2 .form-field__row {
    display: flex;
    gap: var(--rb-s3);
    align-items: stretch;
  }
  .rb2 .form-field__row > :first-child { flex: 1 1 0; min-width: 0; }
  .rb2 .form-field__row > :last-child { flex: 0 0 auto; }
  .rb2 .form-field__row .rz-dropdown-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Two or more equal-width fields side by side. */
  .rb2 .form-fields-row {
    display: flex;
    gap: var(--rb-s3);
  }
  .rb2 .form-field--grow { flex: 1; }

  .rb2 .form-label {
    font-size: var(--rb-text-sm);
    font-weight: 500;
    color: var(--rb-ink);
  }

  .rb2 .form-hint {
    font-size: var(--rb-text-xs);
    color: var(--rb-ink-3);
  }

  .rb2 .form-unit {
    font-size: var(--rb-text-sm);
    color: var(--rb-ink-3);
    white-space: nowrap;
  }

  /* Grouped sub-fields (e.g. the legacy address block). Bordered, not filled — a tinted
     panel clashes inside the white form-section card. */
  .rb2 .form-subgroup {
    display: flex;
    flex-direction: column;
    gap: var(--rb-s3);
    padding: var(--rb-s4);
    border: 1px solid var(--rb-border);
    border-radius: var(--rb-r-sm);
  }

  /* Right-aligned footer action bar. */
  .rb2 .form-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--rb-s2);
    padding-top: var(--rb-s3);
    border-top: 1px solid var(--rb-border);
  }

  @media (max-width: 768px) {
    .rb2 .form-field__row { flex-wrap: wrap; }
    .rb2 .form-fields-row { flex-direction: column; }
  }

  /* ── Mobile app shell (bottom nav + search row) ────────────────────────── */
  /* The nav's height ABOVE its own safe-area padding. The tallest grid cell is the scan FAB
     slot (56px button + 2px gap + label), not a plain nav item — measured at ~89px, which is
     why the 68px this token used to claim left the bottom of every page behind the nav on any
     device whose safe-area inset is 0. Nothing in CSS can ask the nav how tall it is, so the
     12px cushion the consumers below add is what absorbs a font change.
     On .rb2--bottomnav, not .rb2: RbSearchBar's suggestions cap reads it as "how much bottom
     chrome is there" and documents a 0 fallback, which it never got while this sat on .rb2. */
  .rb2.rb2--bottomnav { --rb2-bottomnav-h: 90px; }

  /* The items-list selection dock (.rb-selbar--mobile, RbSelectionBar.razor.css) is
     fixed at bottom: var(--rb-s4) with z-index 1000 — without an offset it lands ON
     the bottom nav. Its scoped css is unlayered, so !important is required. */
  .rb2.rb2--bottomnav .rb-selbar--mobile {
      bottom: calc(var(--rb2-bottomnav-h) + var(--rb-safe-bottom) + var(--rb-s3)) !important;
  }

  /* ItemForm's sticky save row needs NO offset rule here. It is `bottom: 0` in its own CSS and
     rides on the scroll container's padding-bottom — the .rz-body rule below, or the drawer
     body's own — because a bottom-anchored sticky resolves against the CONTENT box. An offset
     on top of that padding is a double count; that is what left the row mid-screen in 2.1.0. */

  /* Radzen's unlayered default.css sets .rz-body padding — !important is required
     to win from inside @layer (existing precedent above). Applied only when the
     bottom nav is actually rendered (.rb2--bottomnav).

     No --rb-safe-bottom term here, unlike the fixed dock above: app.css already gives
     `.rz-layout` a safe-bottom padding, so this scroll container's box stops at the top of the
     gesture strip and the nav's own safe padding covers the rest. Adding it again reserved the
     strip twice — 34px of dead space on an iPhone — while still under-counting the nav itself.
     So: nav height + a 12px gap, and the gap is then the same on every device. */
  .rb2.rb2--bottomnav .rz-body {
      padding-bottom: calc(var(--rb2-bottomnav-h) + var(--rb-s3)) !important;
  }

  /* An <a> to the overview page, now wrapping the logo IMAGE rather than the text lockup
     it used to draw itself. Radzen underlines and recolors anchors by default, so the reset
     stays even though there is no text left to recolour: an underline would still draw under
     the image, and a future text child would inherit the wrong colour.

     text-decoration needs !important for the same reason the old colour did. app.css ships an
     UNLAYERED `a:hover { color: var(--main-primary-color) }`, and for NORMAL declarations an
     unlayered rule beats anything in @layer regardless of specificity. That was the reported
     bug when this was text: a tap left :hover stuck on a phone until the user tapped elsewhere,
     and the wordmark went yellow until a reload. An <img> cannot be recoloured that way, which
     removes the whole class of problem — the reset is kept as a guard, not as a live fix. */
  .rb2 a.rb2-wordmark,
  .rb2 a.rb2-wordmark:link,
  .rb2 a.rb2-wordmark:visited,
  .rb2 a.rb2-wordmark:hover,
  .rb2 a.rb2-wordmark:focus,
  .rb2 a.rb2-wordmark:active {
      display: inline-flex;
      align-items: center;
      color: var(--rb-ink) !important;
      text-decoration: none !important;
  }

  /* Height drives it; the width follows the asset's own 6.31 ink ratio, so 18px renders 114px
     wide. 18px is deliberate: the desktop sidebar sets its <img> to 56px, but that file is mostly
     transparent padding, so what a reader actually sees there is ~17px of ink. Matching it keeps
     the two surfaces consistent, and it sits at the same optical height as the 17px text lockup
     this replaced. `display: block` kills the inline baseline gap that would otherwise offset the
     image inside the flex row. */
  .rb2 a.rb2-wordmark img {
      display: block;
      height: 18px;
      width: auto;
  }

  /* The row blends into the cream canvas so the white search field reads as the
     lighter element; a border-bottom would draw a cream-on-cream line — omit it. */
  .rb2 .rb2-mobile-searchrow {
      padding: 8px 12px 10px;
      background: var(--rb-cream);
  }

  /* RbSearchBar's SCOPED css sets border-radius on .rb2-search-field (unlayered,
     beats @layer) — !important is required for the pill shape.
     NOTE: no display:none rule for .rb2-search-shortcut is needed — the component's
     own CSS already hides the Ctrl+K hint at ≤768px, and this row only renders there. */
  .rb2 .rb2-mobile-searchrow .rb2-search-field {
      border-radius: var(--rb-r-full) !important;
  }


  /* ── Tap-to-dismiss overlays: keep them clickable on iOS ───────────────────
     Blazor listens for `click` on `document` (blazor.webassembly.js registers one
     delegated listener per event name) and walks up from the target. WebKit only
     synthesises a click that reaches `document` when the tapped element looks
     interactive — a link, a form control, or anything computing to
     `cursor: pointer`. A bare backdrop <div> is none of those, so in the iOS app
     every tap outside a sheet, drawer or menu was swallowed and only the Close
     button dismissed it (TestFlight 2.2.0 build 41).

     One rule for all of them: a new overlay that forgets `cursor: pointer` in its
     own scoped css is dead on iOS, and nothing on the desktop side says so.

     `any-pointer: coarse`, not `hover: none`: the latter describes the PRIMARY pointer, so
     an iPad with a Magic Keyboard reports `hover: hover` and would lose the rule entirely
     while its finger taps still go through WebKit's heuristic — the bug would survive on
     the one iOS form factor most likely to be doing stocktaking. The cost is a touchscreen
     laptop, where a mouse now gets a hand cursor over the page while RbSearchBar's
     transparent full-viewport backdrop is up. Cosmetic, and only while the suggestions
     are open.

     `.rz-dialog-mask` needs no `CloseDialogOnOverlayClick` guard: DialogContainer.razor
     (checked at the v11.1.3 tag, the pinned version) gives the mask an inline `pointer-events: none` for every
     dialog that does NOT dismiss on an overlay click, so on those the mask is never the hit
     target and this declaration cannot promise a dismissal that will not happen. */
  @media (any-pointer: coarse) {
    .rb2 .rz-dialog-mask,
    .rb2 .rb-drawer-backdrop,
    .rb2 .rb-selbar-sheet-backdrop,
    .rb2 .rb2-sheet-backdrop,
    .rb2 .rb2-search-backdrop,
    .rb2 .rb2-add-backdrop,
    .rb2 .rb2-sidebar-scrim,
    .rb2 .rb-actions-menu__overlay,
    .rb2 .rb-colpicker__overlay {
      cursor: pointer;
    }
  }


  /* ── Mobile: 16px floor on every form-control text ───────────────────────────
     WebKit force-zooms the viewport whenever a control that is about to receive the
     on-screen keyboard computes to LESS than 16px, and it never zooms back out — the
     page is left scaled and horizontally panned after a single tap into a field, and
     re-pans on every keystroke. Fields had been floored one at a time before (#221 did
     RbSearchBar and the help-centre manual search; RbFilterBar was written floored from
     the start in #193). This rule ends that by flooring the whole DOCUMENT at once,
     including fields added later. It covers the installed Capacitor app: that shell
     loads this very origin (mobile/capacitor.config.ts), so its webview gets this file.

     What was under the floor, measured against the real stylesheet chain at 390px. Two
     Radzen classes and four hand-written rules — six elements in all:
       - `.rz-textbox` and `.rz-textarea`. Both sit on the form control ITSELF, and both
         take font-size from Radzen's shared input rule, which reads --rz-input-font-size
         = var(--rz-body-font-size) = 0.875rem, so they render at 14px. The item-name
         field is a RadzenTextBox and measured 14px.
         NOT "every Radzen input", and specifically NOT the `.rz-numeric` / `.rz-dropdown`
         WRAPPERS: those are divs, this rule never touches them, and the <input> each of
         them wraps was already 16px. Every input does carry a Radzen font-size —
         default.css ships a bare, unlayered `input { font-size: var(--rz-input-font-size) }`
         — but app.css's `input { font-size: 16px }` has the same 0,0,1 specificity and
         loads AFTER default.css, so it already won for anything Radzen did not also match
         by class. `.rz-textbox` (0,1,0) is exactly what beat it.
       - .rb-filter-sidebar__search and .rb2-labels-name-input at var(--rb-text-sm) = 14px.
       - .wh-notif__input at 0.95rem = 15.2px — that is THREE elements (the subject and
         heading inputs and the message textarea) — and .wh-notif__source, the HTML-source
         textarea next to them, at 13px. That last one is a deliberately dense monospace
         code surface in a fixed 260px box: on a phone it does raise the keyboard and so
         belongs under the floor, but on a touch TABLET, where the rest of the page is the
         desktop layout, +23% is a straight loss. If that ever matters, give this one class
         its own `@media (max-width: 768px)` exception rather than widening the rule.

     Why !important, and why INSIDE the layer. app.css already declares
     `html, body, button, input, select, textarea { font-size: 16px }`, but `input` is
     specificity 0,0,1 and loses to Radzen's `.rz-textbox` and to every scoped
     `.razor.css` rule — which is why that rule looks like a fix and never was one. From
     inside @layer rb2 an !important outranks EVERY unlayered !important at any
     specificity: unlayered styles are the implicit LAST layer, and for important
     declarations the layer order reverses, so the first layer wins. Measured in
     Chromium, not assumed — a layered `#a { font-size: 111px !important }` beats an
     unlayered `#a.hi.hi.hi { font-size: 222px !important }`. (The note on the dialog
     block below used to claim the opposite; it is retracted there.) That matters here
     because `textarea` and `select` are specificity 0,0,1 on their own: unlayered, a
     future scoped rule carrying !important would beat them, and two of the fields
     listed above ARE textareas.

     .rz-dropdown-label is the DISPLAY text of a dropdown, not an input — it raises no
     keyboard and cannot trigger the zoom. It is floored purely so a form does not
     alternate 16px inputs with 14px dropdowns down the page (the item form stacks
     project / name / category / condition, and the mix is visible). Options inside the
     open panel are deliberately left alone: that popup is its own surface, and resizing
     list items would change its height and scroll behaviour.

     Headroom: at 16px the glyph ink of Outfit exactly fills the 20px content box of a
     Radzen control (36px height minus 2×7px padding minus 2×1px border) — measured with
     canvas TextMetrics on the live stylesheets, and Polish diacritics and descenders
     render intact. There is no slack left, so do NOT raise this floor further without
     also giving the box more room (lower --rz-input-padding-block — the unsuffixed one, it
     is what the :root line-height calc reads; --rz-input-padding-block-md only feeds the
     calc inside .rz-input-md — rather than growing --rz-input-height, so nothing reflows).
     Measured for real: at 16px the worst case in this app is a Polish capital with a
     diacritic over a descender, whose ink is 16px of ascent + 4px of descent = exactly the
     20px box. 17px would need 21px and would clip.

     16px flat, not a `max()` of the inherited size: nothing in the app declares a
     text-entry control ABOVE 16px, so the floor never shrinks anything today. A control
     that legitimately needs to be larger must say so from inside this block.

     Deliberately NOT fixed with `maximum-scale=1` / `user-scalable=no` on the viewport
     meta: that kills pinch-zoom for everyone (WCAG 1.4.4) and modern iOS Safari ignores
     it anyway.

     Scope is wider than "phones", on purpose: the app's mobile breakpoint OR any
     coarse-pointer/no-hover device. The second half catches a phone in LANDSCAPE (recent
     iPhones are 812-932px wide there, past 768px; an SE at 667px is caught by the first
     half) and every touch tablet at any width. Mouse-driven touchscreen laptops report
     `hover: hover` and keep the desktop 14px control scale.

     The excluded input types never raise a keyboard, so the floor has no purpose on
     them. It would be inert on today's markup anyway (RbLabelManager's swatch is a 24x24px
     type=color, RbDropzone's file input is `inset: 0; width/height: 100%; opacity: 0` — it
     has no rendered text at all — and the app writes button-like controls as <button>,
     never <input type=submit>) — the list is there so the rule keeps meaning what it says
     if that ever changes.

     NOT reachable from here, by construction: a stylesheet never cascades into a nested
     browsing context, same-origin or not. The help centre embedded by TutorialsPage is
     same-origin and still needs its own copy of the floor, which it has
     (wwwroot/manual/index.html, and its source docs/manual/index.html). The Auth0 hosted
     login page is a third-party origin; its fields measured 16px on the live site. */
  @media (max-width: 768px), (hover: none) and (pointer: coarse) {
      input:not([type="button"]):not([type="checkbox"]):not([type="color"]):not([type="file"]):not([type="hidden"]):not([type="image"]):not([type="radio"]):not([type="range"]):not([type="reset"]):not([type="submit"]),
      textarea,
      select,
      [contenteditable]:not([contenteditable="false"]),
      .rz-dropdown-label {
          font-size: 16px !important;
      }
  }

}

/* ── Radzen dialogs vs the system bars ──────────────────────────────────────────
   Unlayered. Radzen's default.css ships, unlayered:

     .rz-dialog { max-height: 100% }
     @media (max-width: 768px) {
       .rz-dialog:not(.rz-dialog-confirm):not(.rz-dialog-alert) {
         position: absolute; width: 100% !important;
         inset-block-start: 0px !important; border-radius: 0 }
     }

   This block used to carry the note "an unlayered `!important` beats a layered
   `!important` regardless of specificity, so `inset-block-start: 0` is unreachable from
   inside the layer". That is BACKWARDS, and the note is retracted. Unlayered styles are
   the implicit LAST layer, and for important declarations the layer order reverses, so a
   LAYERED `!important` outranks an unlayered one at ANY specificity — measured in
   Chromium, and the same mechanism the 16px input floor above now relies on.

   So this block does not have to be unlayered; it simply is, and that is fine, because it
   then fights Radzen unlayered-vs-unlayered, where the usual rules apply again: specificity
   first (Radzen's selector is 0,3,0 — `:not()` contributes its argument — hence the doubled
   class here), then source order, and this file is injected after default.css. The doubled
   class is load-bearing for that fight; leave it.

   Two distinct failures being fixed:
   1. ≤768px Radzen TOP-PINS the dialog at y=0. In the edge-to-edge shell that is under the
      status bar, so the dialog's title and its ✕ are untappable — the same defect as the
      drawer header had. Pin it to the safe inset instead, and take the height budget from
      the top inset once (not twice — it is no longer centred).
   2. `max-height: 100%` means the whole webview, system bars included, so a tall dialog's
      button row lands under the gesture bar. `.rz-dialog-content` is `overflow: auto` in
      Radzen's CSS, so capping the height scrolls rather than clips.

   Confirm/Alert dialogs are excluded from Radzen's mobile rule and stay centred, so they
   keep the centred budget — but they carry `max-width: 400px`, which overflows a ≤400px
   viewport. Radzen ships that as a NORMAL declaration (`.rz-dialog-confirm,.rz-dialog-alert
   {max-width:400px;margin:.75rem}` — no !important), so any !important caps it, layered or
   not; this block being unlayered is not what makes it work. */
.rb2 .rz-dialog.rz-dialog {
    max-width: calc(100vw - 2 * var(--rb-s4, 16px)) !important;
    max-height: calc(100dvh
                     - 2 * max(var(--rb-safe-top, 0px), var(--rb-safe-bottom, 0px))
                     - 2 * var(--rb-s4, 16px)) !important;
}

/* ── Mobile dialogs are bottom sheets ───────────────────────────────────────────
   Radzen 11.1.3's default.css ships this, unlayered:

     @media (max-width: 768px) {
       .rz-dialog:not(.rz-dialog-confirm):not(.rz-dialog-alert) {
         position: absolute; width: 100% !important;
         inset-block-start: 0px !important; border-radius: 0 }
     }

   That top-pins every detail view and form as a square full-width slab flush to the
   top edge — the presentation this block replaces. The app already speaks one mobile
   overlay language (RbSheetShell: bottom-anchored, rounded top, grab handle, 85dvh);
   these rules make the ~40 DialogService call sites speak it too, without touching a
   single call site, because Radzen renders all of them through the same `.rz-dialog`.

   Why unlayered — NOT because the layer cannot reach it. A LAYERED `!important` outranks
   an unlayered one at ANY specificity: unlayered styles are the implicit last layer, and for
   important declarations the layer order reverses, so the first layer wins. That is the same
   mechanism the 16px input floor relies on, and it is measured in Chromium, not assumed. An
   earlier note here claimed the opposite; it is retracted, as it is at the block above.

   This block simply IS unlayered, and that is fine, because it then fights Radzen
   unlayered-vs-unlayered, where the usual rules apply again: `inset-block-start: 0 !important`
   is unlayered, so among important declarations specificity decides (Radzen's selector is
   0,3,0 — `:not()` contributes its argument), then source order. The doubled
   `.rz-dialog.rz-dialog` takes this to 0,5,0 and this file is injected after default.css. The
   doubled class is load-bearing for that fight; leave it.

   Geometry note: `.rz-dialog` is `position: absolute` inside `.rz-dialog-wrapper`,
   which is `position: fixed` at the full viewport — so anchoring to `inset-block-end`
   pins the sheet to the bottom of the screen. In the edge-to-edge native shell that
   bottom is UNDER the gesture bar, hence the safe-inset padding on the content.

   Confirm/Alert keep Radzen's centred placement (mirrored :not() list) — a short
   yes/no prompt reads better centred, and they are excluded from Radzen's own rule. */
@media (max-width: 768px) {
    .rb2 .rz-dialog.rz-dialog:not(.rz-dialog-confirm):not(.rz-dialog-alert) {
        inset-block-start: auto !important;
        inset-block-end: 0 !important;
        inset-inline: 0 !important;
        /* The base rule above caps every dialog at `100vw - 2 * 16px` so a CENTRED dialog
           keeps a gutter. A bottom sheet is full-bleed, and that cap is actively harmful
           here: with `inset-inline: 0` the box is over-constrained, the inline-start edge
           wins, and the leftover 32px becomes a lopsided gap down the right-hand side —
           which also squeezes the trailing action button off the edge. */
        max-width: 100% !important;
        max-height: 85dvh !important;
        border-radius: var(--rb-r-lg, 16px) var(--rb-r-lg, 16px) 0 0;
        box-shadow: 0 -12px 40px rgba(20, 22, 24, .25);
        animation: rb2-sheet-rise var(--rb-t-slow, 280ms) var(--rb-ease, ease);
        /* Radzen tints the titlebar (`--rz-base-100`, #f6f7fa) against a white dialog
           body. Once the grab handle sits ABOVE the titlebar, that tint reads as a white
           strip capping a grey bar — a seam right where the sheet's rounded corners are.
           Flattening the titlebar makes the handle and the title one continuous surface,
           which is what RbSheetShell already looks like. Desktop keeps the tint. */
        --rz-dialog-title-background-color: transparent;
    }

    /* Grab handle. `.rz-dialog` is `display: flex; flex-direction: column`, so this
       pseudo-element becomes its first flex item — above the titlebar, like
       RbSheetShell's `__grab` div. Same 42×5 as the hand-built sheets. */
    .rb2 .rz-dialog.rz-dialog:not(.rz-dialog-confirm):not(.rz-dialog-alert)::before {
        content: "";
        flex: 0 0 auto;
        width: 42px;
        height: 5px;
        margin: 8px auto 4px;
        border-radius: var(--rb-r-full, 999px);
        background: var(--rb-border, #E3DFD4);
    }

    /* The sheet's last row (a form's Save/Cancel) would otherwise sit under the
       Android gesture bar / iPhone home indicator, unreachable even by scrolling. */
    .rb2 .rz-dialog:not(.rz-dialog-confirm):not(.rz-dialog-alert) .rz-dialog-content {
        padding-block-end: calc(var(--rz-dialog-content-padding, 1rem)
                                + var(--rb-safe-bottom, 0px));
    }

    /* Follows the finger during a swipe-to-dismiss drag (see js/rb-sheet.js). The
       module sets --rb-sheet-drag and clears it on release; the transition is
       suppressed while dragging so the sheet tracks the touch 1:1. */
    .rb2 .rz-dialog.rz-dialog--rb-dragging {
        animation: none !important;
        transition: none !important;
        transform: translateY(var(--rb-sheet-drag, 0px)) !important;
    }

    .rb2 .rz-dialog.rz-dialog--rb-settling {
        animation: none !important;
        transition: transform var(--rb-t-base, 180ms) var(--rb-ease, ease) !important;
        transform: translateY(var(--rb-sheet-drag, 0px)) !important;
    }
}

@keyframes rb2-sheet-rise {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}
