/* gogridgo – default stylesheet for all gg-* classes.
   Override any rule by scoping a custom class:
     .my-grid .gg-th { ... }
   or pass it via Grid.CSSClass("my-grid"). */

.gg-grid { font-size: 0.875rem; font-family: inherit; }

/* File-drop target highlight — applied to the grid root while a drag hovers it
   (see Grid.OnDropFile). Blue dashed outline + faint tint, drawn inside the box
   (outline-offset:-2px) so it never shifts layout. */
.gg-dropzone-hover {
  outline: 2px dashed #3b82f6;
  outline-offset: -2px;
  background: rgba(59, 130, 246, 0.05);
}

.gg-caption {
  font-weight: 600;
  margin-bottom: 0.5rem;
  font-size: 0.875rem;
}

.gg-table-scroll {
  overflow-x: auto;
  width: 100%;
}

/* Filter-mode affordance — the pointer cursor plus a stronger tint on the
   hovered CELL (over the subtle row hover). It must promise EXACTLY what a
   click will do, so the two conditions mirror filterui.js's
   __ggCellFilterTakes: the ⚙ option is on and the filter row is open (a
   plain click filters), or Alt/Opt is held right now (filterui.js puts
   .gg-alt on <html> while the key is down, which also PREVIEWS the gesture:
   hold Alt and the cells light up before you commit).

   Keyed on markers rather than on the filter row alone — that older rule
   kept promising a filter after a plain click stopped applying one.

   :not(.gg-td-summary) matters as much here as in the hover rule below.
   filterui.js refuses to filter from a totals cell, and since the "above"
   totals row moved into <thead> it is no longer excluded by the tbody scope,
   so without this the sums grew a pointer cursor over a dead click. */
html.gg-alt .gg-grid[data-gg-adv-filters]:not([data-rowclick]) td.gg-td:not(.gg-td-summary),
html.gg-cellfilter .gg-grid[data-gg-adv-filters]:not([data-rowclick]):has(.gg-filter-row:not([hidden])) td.gg-td:not(.gg-td-summary) {
  cursor: pointer;
}

html.gg-alt .gg-grid[data-gg-adv-filters]:not([data-rowclick]) tbody td.gg-td:hover:not(.gg-td-summary),
html.gg-cellfilter .gg-grid[data-gg-adv-filters]:not([data-rowclick]):has(.gg-filter-row:not([hidden])) tbody td.gg-td:hover:not(.gg-td-summary) {
  background: #e0e7ff; /* indigo-100 — the filter family's tint */
}

/* Hide the native in-control search-cancel ✕: the advanced filter bar has
   an explicit per-column clear button in its OWN row now, so the browser
   affordance would only cramp long values and duplicate the action. The
   bar button cannot widen the column (it sits above the box), so this does
   not reintroduce the layout shift that once forced the native ✕ back. */
.gg-filter-input::-webkit-search-cancel-button {
  display: none;
  -webkit-appearance: none;
}

/* Force a classic (non-overlay) horizontal scrollbar in WebKit/Blink —
   Chrome's overlay bar stays invisible until hover, leaving wide grids
   with no scrolling affordance at the bottom edge. Styling any
   ::-webkit-scrollbar part opts the element out of overlay mode. */
.gg-table-scroll::-webkit-scrollbar {
  height: 10px;
}

.gg-table-scroll::-webkit-scrollbar-track {
  background: #f3f4f6;
  border-radius: 5px;
}

.gg-table-scroll::-webkit-scrollbar-thumb {
  background: #c7ccd4;
  border-radius: 5px;
}

.gg-table-scroll::-webkit-scrollbar-thumb:hover {
  background: #9ca3af;
}

.gg-table {
  width: 100%;
  border-collapse: collapse;
}

.gg-th {
  padding: 0.5rem 0.75rem;
  text-align: left;
  vertical-align: middle;
  white-space: nowrap;
  font-weight: 500;
  border-bottom: 1px solid #e5e7eb;
  background: #e5e7eb;
  color: #374151;
  font-size: 0.75rem;
}

.gg-th-sortable { cursor: pointer; user-select: none; }
.gg-th-sortable:hover { background: #d1d5db; }

.gg-sort-ind {
  display: inline-block;
  margin-left: 0.125rem;
  font-size: 0.625rem;
  color: #6b7280;
}

.gg-sort-ind sup {
  font-size: 0.5625rem;
  color: #9ca3af;
  margin-left: 0.0625rem;
}

.gg-filter-row .gg-filter-th {
  padding: 0.25rem 0.5rem;
  background: #f3f4f6;
  border-bottom: 1px solid #e5e7eb;
}

.gg-filter-input {
  width: 100%;
  /* min-width:0 is load-bearing: a search/select input has a non-zero
     intrinsic min-content width (~150px). In an auto-layout table that
     min-content bumps the column's minimum the moment the filter row
     becomes visible, stealing slack from a fit-to-container table and
     nudging the header ~1px. Zeroing it lets the input track the column
     width set by the header/data, so toggling the filter row never
     reflows the columns. */
  min-width: 0;
  padding: 0.1875rem 0.375rem;
  font-size: 0.75rem;
  font-family: inherit;
  border: 1px solid #d1d5db;
  border-radius: 0.25rem;
  background: #fff;
  color: #374151;
  box-sizing: border-box;
}

.gg-filter-input:focus {
  outline: none;
  border-color: #3b82f6;
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

.gg-filter-select { cursor: pointer; padding-right: 1.25rem; }

/* Two-selector specificity so this beats `.gg-filter-row .gg-filter-th`
   (0,2,0) — otherwise the wider 0.5rem padding wins and the clear-all cell
   makes the selection/row-number column wider than its header checkbox,
   nudging the whole header right when the filter row appears. */
.gg-filter-row .gg-filter-leading { padding: 0.25rem 0.25rem; text-align: center; }

.gg-filter-clear-all {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Match the selection checkbox width (.gg-sel-cb = 1.125rem). The clear-all
     button is the only content in the leading filter cell; if it's wider than
     the header checkbox it grows the selection column the instant the filter
     row appears, nudging the whole header right (~2px). */
  width: 1.125rem;
  height: 1.125rem;
  padding: 0;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #6b7280;
}

.gg-filter-clear-all:hover { background: #e5e7eb; color: #b91c1c; }
.gg-filter-clear-all svg { width: 0.875rem; height: 0.875rem; }

.gg-pager-pick {
  font-size: 0.75rem;
  font-family: inherit;
  padding: 0.125rem 0.375rem;
  border: 1px solid #d1d5db;
  border-radius: 0.25rem;
  background: #fff;
  color: #374151;
  cursor: pointer;
}

.gg-pager-pick:focus {
  outline: none;
  border-color: #3b82f6;
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

/* Export menu — visually matches xml.pro's top-bar hamburger
   (DaisyUI `dropdown` + `menu menu-sm bg-base-100 rounded-box
   shadow-md border border-base-200`). gogridgo can't depend on
   DaisyUI directly, so the visuals are hand-tuned to the same
   colour palette / radii / shadows. <details>/<summary> drives
   open state natively; click-outside JS lives in render.go.

   Trigger sized to btn-xs btn-square equivalent (1.5rem square)
   so the toolbar height matches a row with .gg-filter-toggle. */
.gg-export-menu {
  display: inline-flex; align-items: center;
  position: relative;
  margin-left: 0.25rem;
}
.gg-export-menu > summary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem;
  padding: 0;
  list-style: none; cursor: pointer;
  color: #6b7280;                         /* base-content/60 */
  background: transparent;
  border-radius: 0.5rem;                  /* rounded-box */
  transition: background 0.12s;
}
/* Triangle suppression — three selectors cover every browser:
   list-style for Firefox/standards, ::-webkit-details-marker for
   older Safari/Chrome, ::marker for modern engines. */
.gg-export-menu > summary::-webkit-details-marker { display: none; }
.gg-export-menu > summary::marker { content: ""; }
.gg-export-menu > summary:hover { background: #e5e7eb; color: #374151; }
.gg-export-menu > summary svg { width: 14px; height: 14px; display: block; }

.gg-export-list {
  position: absolute; top: calc(100% + 0.5rem); right: 0;
  z-index: 50;
  margin: 0; padding: 0.5rem;
  min-width: 7rem;
  list-style: none;
  background: #fff;                       /* base-100 */
  border: 1px solid #e5e7eb;              /* base-200 */
  border-radius: 0.5rem;                  /* rounded-box */
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
              0 2px 4px -2px rgba(0, 0, 0, 0.1);  /* shadow-md */
}
/* Bottom pager (the .gg-pager that follows .gg-table-scroll) opens its
   export dropdown UPWARD instead of downward. Without this, a consumer
   wrapping the grid in an overflow:hidden container (e.g. for rounded
   corners) clips the menu — the icon "works" only in the top toolbar.
   Opening up keeps the menu inside the container in every layout. */
.gg-table-scroll ~ .gg-pager .gg-export-list {
  top: auto;
  bottom: calc(100% + 0.5rem);
}
.gg-export-list li { margin: 0; padding: 0; }
.gg-export-item {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem; line-height: 1.25rem;
  color: #1f2937;                         /* base-content */
  text-decoration: none;
  border-radius: 0.375rem;                /* menu item rounding */
}
.gg-export-item:hover { background: #e5e7eb; }

/* Settings dropdown (⚙) — mirrors .gg-export-menu. Hosts the rows-per-page
   picker (moved out of the toolbar) and the case-sensitivity filter toggle. */
.gg-settings-menu {
  display: inline-flex; align-items: center;
  position: relative;
  margin-left: 0.25rem;
}
.gg-settings-menu > summary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem;
  padding: 0;
  list-style: none; cursor: pointer;
  color: #6b7280;                         /* base-content/60 */
  background: transparent;
  border-radius: 0.5rem;                  /* rounded-box */
  transition: background 0.12s;
}
.gg-settings-menu > summary::-webkit-details-marker { display: none; }
.gg-settings-menu > summary::marker { content: ""; }
.gg-settings-menu > summary:hover { background: #e5e7eb; color: #374151; }
.gg-settings-menu > summary svg { width: 14px; height: 14px; display: block; }

.gg-settings-panel {
  position: absolute; top: calc(100% + 0.5rem); right: 0;
  z-index: 50;
  margin: 0; padding: 0.75rem;
  /* 14rem wrapped every second label ("Zagęszczony układ (jak w Excelu)",
     "Filtruj po kliku w komórce") into two ragged lines; the panel carries
     more options now, so give it room to list them one per line. */
  min-width: 20rem;
  display: flex; flex-direction: column; gap: 0.625rem;
  background: #fff;                       /* base-100 */
  border: 1px solid #e5e7eb;              /* base-200 */
  border-radius: 0.5rem;                  /* rounded-box */
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
              0 2px 4px -2px rgba(0, 0, 0, 0.1);  /* shadow-md */
  font-size: 0.875rem; line-height: 1.25rem;
  color: #1f2937;                         /* base-content */
  text-align: left;
}
/* Bottom pager opens the panel UPWARD (same reasoning as the export menu). */
.gg-table-scroll ~ .gg-pager .gg-settings-panel {
  top: auto;
  bottom: calc(100% + 0.5rem);
}
.gg-settings-row {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
}
.gg-settings-row .gg-pager-pick { margin: 0; }
.gg-settings-group {
  display: flex; flex-direction: column; gap: 0.375rem;
}
/* Divider above the group only when a paging row precedes it. */
.gg-settings-group:not(:first-child) {
  border-top: 1px solid #f0f0f0;
  padding-top: 0.625rem;
}
.gg-settings-group-label {
  font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: #9ca3af; font-weight: 500;
}
.gg-settings-check {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer;
}
.gg-settings-check input { cursor: pointer; }

.gg-filter-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  margin-right: 0.375rem;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #6b7280;
}

.gg-filter-toggle:hover { background: #e5e7eb; color: #374151; }
.gg-filter-toggle-on { background: #dbeafe; color: #1e40af; }
.gg-filter-toggle-on:hover { background: #bfdbfe; color: #1e3a8a; }
.gg-filter-toggle svg { width: 0.875rem; height: 0.875rem; }

/* Funnel with ACTIVE filters — inverted indigo (matches the advanced
   filter cells' gg-fop-btn-on), so filtered data is signalled even when
   the filter row itself is hidden. Wins over the plain row-open state. */
.gg-filter-toggle-filtered,
.gg-filter-toggle-filtered.gg-filter-toggle-on { background: #4f46e5; color: #fff; }
.gg-filter-toggle-filtered:hover { background: #4338ca; color: #fff; }

/* Filter-summary bar — sits under the top band while any filter is
   active: human-readable description of every column filter, with the
   reset-all ✕ at its left. Server-rendered, so it survives every swap. */
/* Lives INSIDE the toolbar, between the row counter and whatever comes next
   (pager buttons, or the export/settings cluster in fullscreen). flex:1 with
   min-width:0 is what makes it "end where the buttons begin" without knowing
   which of them is there; the text ellipsizes rather than wrapping the bar
   onto a second line, which would reintroduce the very jump this move was
   meant to remove. The full sentence stays available as a tooltip. */
.gg-filter-summary {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  flex: 1 1 auto;
  min-width: 0;
  /* Fixed height, matching the toolbar buttons: the chip must not be the
     tallest thing in the bar, or applying a filter grows the toolbar by a
     pixel and the table below still twitches — the whole point of moving it
     up here was that nothing moves. */
  height: 1.5rem;
  padding: 0 0.5rem;
  font-size: 0.75rem;
  color: #3730a3;
  background: #eef2ff;
  border-radius: 0.375rem;
}

.gg-fsum-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gg-fsum-and {
  color: #818cf8;
  font-style: italic;
}

/* The filtered VALUES pop out of the sentence — warm brown, bold. */
.gg-fsum-val {
  color: #9a3412;
  font-weight: 600;
}

/* Reset-all button — lives at the left edge of the summary bar; clears
   every column's filter and keeps the filter row open. */
.gg-filter-reset {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  margin-right: 0.375rem;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #6b7280;
}

/* …and its reset button shrinks to fit that height. */
.gg-filter-summary .gg-filter-reset {
  width: 1.125rem;
  height: 1.125rem;
  margin-right: 0.25rem;
}

.gg-filter-reset:hover { background: #fef2f2; color: #b91c1c; }
.gg-filter-reset svg { width: 0.875rem; height: 0.875rem; }

/* Maximize toggle — expands the grid into a viewport-filling overlay. */
.gg-maximize-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #6b7280;
}

.gg-maximize-toggle:hover { background: #e5e7eb; color: #374151; }
.gg-maximize-toggle svg { width: 0.875rem; height: 0.875rem; }

/* Copy-page button (Grid.CopyPage). Same box, same icon size, same colours as
   the export menu and the fullscreen toggle beside it: the four controls read
   as one set, and a control that sizes itself is the one the eye trips over. */
.gg-copy-page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #6b7280;
}
.gg-copy-page:hover { background: #e5e7eb; color: #374151; }
.gg-copy-page svg { width: 0.875rem; height: 0.875rem; display: block; }

/* Fullscreen mode: the grid becomes a fixed overlay; .gg-table-scroll is
   the viewport-sized scroll container for BOTH axes, so its native
   scrollbars sit at the screen edges (and stay visible — see the
   ::-webkit-scrollbar rules above). That same container is what makes
   position:sticky on the thead finally have something to stick to. */
.gg-grid.gg-maximized {
  position: fixed;
  inset: 0;
  z-index: 90;
  margin: 0;
  border-radius: 0;
  background: #fff;
  display: flex;
  flex-direction: column;
  padding: 0.5rem;
  overflow: auto;
}

.gg-maximized .gg-table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.gg-maximized .gg-table-scroll::-webkit-scrollbar {
  width: 10px; /* vertical bar too, matching the horizontal one */
}

/* Header (labels + filter row + totals) pinned while scrolling rows. Sticky on
   the whole <thead> — supported by current Chrome/Firefox — so the rows pin as
   one block and nobody has to compute a per-row top offset against a header
   whose height moves with the funnel, the density toggle and header wrapping.

   The separated border model is what makes that safe. Under
   border-collapse:collapse a sticky <thead> in Chrome paints the scrolled rows
   THROUGH its own cell backgrounds — the header stayed put but text from the
   body ghosted across it, worst in the widest column. Opaque backgrounds on the
   cells do not help: the bleed happens in the collapsed-border painting path,
   not for want of a background. This is the same Chrome weakness the header
   gridlines already work around by drawing themselves as inset shadows.

   border-spacing:0 keeps the geometry identical to the collapsed model here:
   the cells only ever draw a bottom and a right border, so each boundary still
   resolves to exactly one 1px line, with no doubling and no width change on
   entering fullscreen. Scoped to the two places where the body scrolls under
   its own header — fullscreen and a grid with Grid.BodyHeight set — so an
   ordinary grid keeps the collapsed model untouched. */
.gg-maximized .gg-table,
.gg-body-capped .gg-table {
  border-collapse: separate;
  border-spacing: 0;
}

.gg-maximized .gg-table thead,
.gg-body-capped .gg-table thead {
  position: sticky;
  top: 0;
  z-index: 20;
}

.gg-maximized .gg-table thead th,
.gg-body-capped .gg-table thead th {
  background: #f9fafb;
}

/* The cap itself is per-grid — Grid.BodyHeight writes
   #id:not(.gg-maximized) .gg-table-scroll{max-height:…} into the grid's own
   <style>, where the :not() is what lets fullscreen out of the cap despite the
   id outweighing every selector here. What belongs to every capped grid is the
   second axis. */
.gg-body-capped .gg-table-scroll {
  overflow-y: auto;
}

/* The host-header offset points at chrome that the overlay covers.
   flex:none because .gg-maximized is a column flex container and the bar,
   as a shrinkable item next to .gg-table-scroll (flex:1 1 auto), was being
   squeezed to ~0px — the mirror existed but was invisible in fullscreen. */
.gg-maximized .gg-top-scroll-bar {
  top: 0;
  flex: none;
}

/* Same squeeze as the mirror bar above: .gg-maximized is a column flex
   container, so the action bar has to opt out of shrinking. */
.gg-maximized .gg-act-bar { flex: none; }

/* Collapse/expand chevron — sits at the left of the top band, before the
   filter toggle. Sized like .gg-filter-toggle so the band height matches. */
.gg-collapse-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #6b7280;
  transition: background 0.12s, color 0.12s;
}
.gg-collapse-toggle:hover { background: #e5e7eb; color: #374151; }
.gg-collapse-toggle svg { width: 0.875rem; height: 0.875rem; transition: transform 0.15s; }
/* Collapsed → chevron points right. */
.gg-grid.gg-collapsed .gg-collapse-toggle svg { transform: rotate(-90deg); }

/* Centered title in the top band. Absolutely positioned so it stays centered
   regardless of the left (toggles + count) and right (pager/export) clusters. */
.gg-pager-title {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.8125rem;
  font-weight: 600;
  color: #374151;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 50%;
  pointer-events: none;
}

/* Collapsed grid → only the top band remains (table + footer pager hidden;
   the filter row lives in the table head, so it hides with the table). */
.gg-grid.gg-collapsed .gg-table-scroll,
.gg-grid.gg-collapsed .gg-act-bar,
.gg-grid.gg-collapsed .gg-table-scroll ~ .gg-pager { display: none; }

.gg-td {
  padding: 0.375rem 0.75rem;
  border-bottom: 1px solid #f9fafb;
  color: #374151;
}

/* Excel-like dense mode (Grid.Dense — reports and raw SQL table previews):
   minimal padding + full row AND column gridlines, so many-column data
   views fit far more on screen. Opt-in; list-like grids stay roomy.
   .gg-grid.gg-dense (0,3,0) deliberately outweighs host THEME overrides of
   .gg-td/.gg-th (e.g. xml.pro's .gg-xmlpro .gg-td padding), which load
   after this stylesheet. */
.gg-grid.gg-dense .gg-td {
  padding: 0.125rem 0.375rem;
  border-bottom: 1px solid #e5e7eb;
  border-right: 1px solid #e5e7eb;
}
/* Header gridlines are drawn as INSET SHADOWS, not borders. In fullscreen the
   head is position:sticky, and Chrome drops collapsed borders on a sticky row
   the moment the table starts scrolling — the whole header went flat grey
   mid-scroll. A shadow is painted with the element itself, so it survives.
   Colour is clearly darker than the body gridlines: the header cells sit on a
   grey background where lighter greys (#e5e7eb, #d1d5db) washed out. */
.gg-grid.gg-dense .gg-th {
  padding: 0.25rem 0.375rem;
  box-shadow: inset -1px 0 0 #9ca3af;
}
.gg-grid.gg-dense .gg-filter-row .gg-filter-th {
  padding: 0.125rem 0.25rem;
  box-shadow: inset -1px 0 0 #9ca3af;
}
/* Strong line under the whole header block, in the same colour as the column
   separators — the eye needs a clear break between "controls" and "data".
   Same shadow trick and the same reason as above; the vertical separator is
   repeated here because a second box-shadow rule would replace it, not add to
   it. Which row is last depends on the funnel, hence :has() on the filter
   row's hidden attribute. */
.gg-grid.gg-dense .gg-table thead tr:first-child > .gg-th,
.gg-grid.gg-dense .gg-table thead .gg-filter-row:not([hidden]) > .gg-th {
  box-shadow: inset -1px 0 0 #9ca3af, inset 0 -2px 0 #9ca3af;
}
.gg-grid.gg-dense .gg-table thead:has(.gg-filter-row:not([hidden])) tr:first-child > .gg-th {
  box-shadow: inset -1px 0 0 #9ca3af;
}
.gg-grid:not(.gg-dense) .gg-table thead tr:first-child > .gg-th,
.gg-grid:not(.gg-dense) .gg-table thead .gg-filter-row:not([hidden]) > .gg-th {
  box-shadow: inset 0 -2px 0 #d1d5db;
}
.gg-grid:not(.gg-dense) .gg-table thead:has(.gg-filter-row:not([hidden])) tr:first-child > .gg-th {
  box-shadow: none;
}
/* A SummaryAbove row renders as a third head row, so IT is now the bottom of
   the header block and the closing line moves down to it. Same :has() shape as
   the filter-row rules above — the label row and the filter row give the line
   up and keep only their column separator. */
.gg-grid.gg-dense .gg-table thead:has(.gg-row-summary) tr:first-child > .gg-th,
.gg-grid.gg-dense .gg-table thead:has(.gg-row-summary) .gg-filter-row:not([hidden]) > .gg-th {
  box-shadow: inset -1px 0 0 #9ca3af;
}
.gg-grid:not(.gg-dense) .gg-table thead:has(.gg-row-summary) tr:first-child > .gg-th,
.gg-grid:not(.gg-dense) .gg-table thead:has(.gg-row-summary) .gg-filter-row:not([hidden]) > .gg-th {
  box-shadow: none;
}

.gg-grid.gg-dense .gg-td:last-child,
.gg-grid.gg-dense .gg-th:last-child {
  border-right: none; /* container frame already draws the outer line */
}

.gg-row:hover .gg-td { background: #f9fafb; }
.gg-grid[data-rowclick] .gg-row { cursor: pointer; }

.gg-row-summary { cursor: default !important; }
.gg-row-summary > .gg-td-summary {
  font-weight: 600;
  background: #f3f4f6;
  border-top: 2px solid #d1d5db;
  border-bottom: 2px solid #d1d5db;
  color: #1f2937;
}
.gg-row-summary:hover > .gg-td-summary { background: #f3f4f6; }

/* Totals rendered inside <thead> (SummaryAbove). Two changes against the
   tbody variant above: the 2px borders become inset shadows, because a
   collapsed border on a sticky head is exactly what Chrome drops mid-scroll
   (see the header gridline comment further up); and the row carries the
   closing line of the header block plus a hairline separating it from the
   filter row, so totals read as part of the header rather than as the first
   data row. */
.gg-table thead > .gg-row-summary > .gg-td-summary {
  border-top: none;
  border-bottom: none;
  box-shadow: inset 0 1px 0 #e5e7eb, inset 0 -2px 0 #d1d5db;
}
.gg-grid.gg-dense .gg-table thead > .gg-row-summary > .gg-td-summary {
  padding: 0.125rem 0.375rem;
  box-shadow: inset -1px 0 0 #9ca3af, inset 0 1px 0 #d1d5db, inset 0 -2px 0 #9ca3af;
}

/* Click-to-copy on a total. The attribute is the whole gate: render.go emits
   data-gg-copy only on cells that actually carry an aggregate, so the label
   ("Razem:") and the gutters keep the arrow while the numbers get the hand.

   This deliberately outweighs the inherited `cursor: default !important` on
   .gg-row-summary above — !important only decides between declarations for the
   SAME element, and that one sits on the <tr>. Which is exactly the trap that
   let the filter affordance promise a click here in the first place. */
.gg-td-summary[data-gg-copy] {
  cursor: pointer;
}
.gg-row-summary > .gg-td-summary[data-gg-copy]:hover {
  background: #e5e7eb;
}

/* The copy itself is silent — no flash, no caption. Confirming a copy is a
   presentation concern and belongs in something detachable, not wired into the
   grid's own stylesheet; the cursor and the title already say what the click
   does. */

.gg-rn-th {
  width: 2.5rem;
  padding: 0.5rem 0.5rem;
}

.gg-rn {
  text-align: right;
  font-size: 0.75rem;
  color: #9ca3af;
  padding: 0.375rem 0.5rem;
  font-variant-numeric: tabular-nums;
}

.gg-pager {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  /* nowrap since the filter summary moved in: a wrapping toolbar would grow a
     second line and shift the table down — exactly the flicker we removed. */
  flex-wrap: nowrap;
  border-top: 1px solid #f3f4f6;
  position: relative;            /* anchor for the centered .gg-pager-title */
}

.gg-pager-info {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.75rem;
  color: #9ca3af;
}
/* The list-ordered glyph replaces the word "wierszy:" (label kept in the title). */
.gg-pager-info svg { width: 14px; height: 14px; }

.gg-pager-right {
  display: flex;
  align-items: center;
  gap: 0.75rem;                  /* a touch more air between clusters */
  margin-left: auto;
  flex-wrap: nowrap;
  flex: none;                    /* the summary takes the slack, not this */
  /* A floor under the height, because this cluster is what sets the toolbar's:
     the page links (1.625rem) are its tallest item, and a filter that narrows
     the set to a single page removes them — the toolbar would then shrink by
     2px and the table twitch upwards, which is the flicker we came to remove.
     Pinned HERE rather than on .gg-pager because this box has no padding or
     border, so it behaves the same under either box-sizing. */
  min-height: 1.625rem;
}

/* Page links, current page and the disabled arrows all sized to the same
   1.5rem box as the toolbar buttons. Their natural height was a fractional
   26.38px, which made the toolbar (and therefore the table) move by a third
   of a pixel whenever a filter narrowed the set to a single page and the
   links disappeared. Everything in the bar is one height now, so nothing
   below it can twitch. */
.gg-page-link,
.gg-page-current,
.gg-page-disabled,
.gg-page-gap {
  display: inline-flex;
  align-items: center;
  height: 1.5rem;
  box-sizing: border-box;
}

.gg-pager-nav {
  display: flex;
  gap: 0.25rem;
  flex-wrap: wrap;
  align-items: center;
  /* Separate the page navigator from the export/settings icon buttons that
     follow it in .gg-pager-right. */
  margin-right: 0.5rem;
}

/* Page navigator items read as buttons — a delicate border makes the clickable
   pages obvious (they were borderless text before) and sets them apart from the
   icon buttons on the right. */
.gg-page-link {
  display: inline-block;
  padding: 0.2rem 0.5rem;
  border-radius: 0.25rem;
  border: 1px solid #e5e7eb;
  text-decoration: none;
  cursor: pointer;
  color: inherit;
  font-size: 0.75rem;
}

.gg-page-link:hover { background: #f3f4f6; border-color: #d1d5db; }

.gg-page-current {
  display: inline-block;
  padding: 0.2rem 0.5rem;
  border-radius: 0.25rem;
  border: 1px solid #3b82f6;
  background: #3b82f6;
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
}

.gg-page-disabled {
  display: inline-block;
  padding: 0.2rem 0.5rem;
  border-radius: 0.25rem;
  border: 1px solid #f3f4f6;
  font-size: 0.75rem;
  color: #d1d5db;
  cursor: default;
}

.gg-page-gap {
  display: inline-block;
  padding: 0.25rem 0.125rem;
  font-size: 0.75rem;
  color: #9ca3af;
}

.gg-act-th {
  width: 1px; /* shrink to fit content */
  padding: 0.5rem 0.75rem;
  white-space: nowrap;
  text-align: right;
}

.gg-act-td {
  white-space: nowrap;
  padding: 0.25rem 0.5rem;
  text-align: right;
}

.gg-act-btn,
.gg-bulk-btn,
.gg-act-bar-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.25rem 0.125rem;
  border: none;
  background: transparent;
  border-radius: 0.25rem;
  cursor: pointer;
  color: #4b5563;
  font-size: 0.75rem;
  line-height: 1;
}

.gg-act-btn:hover,
.gg-bulk-btn:hover:not(:disabled) { background: #f3f4f6; color: #1f2937; }
/* Inline actions — a row's own buttons in the action cell and the bulk
   buttons in that column's header — read as real buttons (border, white
   face), exactly like the toolbar (.gg-act-bar) does. Bare icon-and-text
   looked like plain labels next to each other. Direct children only: the
   same markup inside .gg-act-menu keeps its flat menu-row look. */
.gg-act-td > .gg-act-btn,
.gg-act-th > .gg-bulk-btn {
  padding: 0.25rem 0.5rem;
  border: 1px solid #e5e7eb;
  background: #fff;
}
.gg-act-td > .gg-act-btn:hover,
.gg-act-th > .gg-bulk-btn:hover:not(:disabled) { background: #f3f4f6; border-color: #9ca3af; color: #1f2937; }
.gg-act-td > .gg-act-btn + .gg-act-btn,
.gg-act-th > .gg-bulk-btn + .gg-bulk-btn { margin-left: 0.25rem; }

.gg-bulk-btn:disabled { opacity: 0.3; cursor: not-allowed; }

/* ActionsAsBar: the row actions as a standing bar above the table. No
   :disabled rule on purpose — in this mode the buttons are always live and
   the "exactly one row" check answers on click, in .gg-act-bar-msg. */
.gg-act-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.375rem;
  padding: 0.375rem 0.75rem;
  border-top: 1px solid #f3f4f6;
}

/* Selection actions that joined the bar wear the same button as the row
   actions — scoped to .gg-act-bar so the bulk buttons in every other grid's
   action-column header keep their borderless header look. Their
   :disabled rule above still greys them until a row is checked. */
.gg-act-bar-btn,
.gg-act-bar .gg-bulk-btn {
  padding: 0.25rem 0.5rem;
  /* Half again as tall as the natural 24px (14px icon + 2×0.25rem + 2×1px
     border): a bar of screen-level verbs has to read as a toolbar, not as a
     row of labels. min-height rather than fatter padding, so a caption-only
     button lines up with an icon one. Horizontal padding stays — the captions
     already set the width, and widening would push the bar onto a second row. */
  min-height: 2.25rem;
  border: 1px solid #e5e7eb;
  background: #fff;
}

.gg-act-bar-btn:hover,
.gg-act-bar .gg-bulk-btn:hover:not(:disabled) { background: #f3f4f6; border-color: #9ca3af; color: #1f2937; }

/* The upright sibling of .gg-act-menu-sep — same job, turned by 90°. */
.gg-act-bar-sep {
  width: 1px;
  align-self: stretch;
  margin: 0.125rem 0.25rem;
  background: #e5e7eb;
}

/* Doubled where the bar changes subject: everything left of it acts on one
   row, everything right of it on the checked set. A single line already means
   "group boundary" here (SeparatorBefore), so this one says more by saying it
   twice. */
.gg-act-bar-sep-strong {
  position: relative;
  margin-left: 0.5rem;
  margin-right: 0.5rem;
}

.gg-act-bar-sep-strong::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3px;
  width: 1px;
  background: #e5e7eb;
}

/* Reserved from the start (visibility, not display/hidden): the message
   appears without shifting the button the reader just aimed at.

   Red, not amber: this is the bar's only channel for "that did not happen",
   and it has to carry across a row of five dark-grey captions. The amber it
   started as (#b45309) read as brown next to them and was easy to miss.
   Weight 500 for the same reason — the text is 0.75rem and stands alone. */
.gg-act-bar-msg {
  visibility: hidden;
  margin-left: 0.5rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1;
  color: #dc2626;
}

/* ActionsAsMenu: per-row actions collapsed behind a single "…" trigger.
   The action buttons inside .gg-act-menu keep their .gg-act-btn markup
   (so dispatch + any pointer-events disable rule still apply); only the
   layout changes — full-width rows with the caption shown. */
.gg-act-menu-wrap { position: relative; display: inline-block; }
/* The "…" trigger reads as a real button — a bordered, rounded rectangle like
   the page's top-right hamburger — rather than a bare glyph. */
.gg-act-menu-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0.25rem 0.4rem; border: 1px solid #d1d5db; background: #fff;
  border-radius: 0.375rem; cursor: pointer; color: #4b5563; line-height: 1;
  transition: background .12s, border-color .12s, color .12s;
}
.gg-act-menu-btn:hover { background: #f3f4f6; border-color: #9ca3af; color: #1f2937; }
.gg-act-menu-btn svg { width: 1rem; height: 1rem; }
.gg-act-menu {
  /* position:fixed (viewport-anchored, JS sets top/left) so the menu escapes
     the .gg-table-scroll clip — overflow-x:auto forces overflow-y to auto, so
     an absolutely-positioned menu opening past the table edge gets cut off /
     hidden behind the header band. Fixed + a high z-index floats it above. */
  display: none; position: fixed; z-index: 1000;
  min-width: 12rem; padding: 0.25rem;
  background: #fff; border: 1px solid #e5e7eb; border-radius: 0.5rem;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
}
.gg-act-menu-open .gg-act-menu { display: block; }
.gg-act-menu .gg-act-btn,
.gg-act-menu .gg-bulk-btn {
  display: flex; width: 100%; justify-content: flex-start; gap: 0.5rem;
  padding: 0.375rem 0.5rem; border-radius: 0.375rem;
  font-size: 0.8125rem; white-space: nowrap;
}
.gg-act-menu .gg-act-btn svg,
.gg-act-menu .gg-bulk-btn svg { width: 0.875rem; height: 0.875rem; flex-shrink: 0; }
.gg-act-menu-sep { height: 1px; margin: 0.25rem 0.25rem; background: #e5e7eb; }

.gg-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  border-radius: 9999px;
  font-size: 0.6875rem;
  line-height: 1.25;
  white-space: nowrap;
}

.gg-badge svg { width: 0.75rem; height: 0.75rem; flex-shrink: 0; }

.gg-badge-ok      { background: #dcfce7; color: #166534; }
.gg-badge-err     { background: #fee2e2; color: #991b1b; }
.gg-badge-pending { background: #fef3c7; color: #92400e; }
.gg-badge-info    { background: #dbeafe; color: #1e40af; }

/* Data bar (Column.DataBar) — a proportional bar behind the cell text, scaled
   to the column's maximum over the whole filtered set.

   It is a BACKGROUND on a block-level span, not a box: an inline-block inside
   a table-layout cell changes the row's metrics and breaks vertical alignment
   between columns, so a decoration that shifts the grid is no decoration. As a
   background it inherits the cell's own text-align, which is why a
   right-aligned number ends up read against a bar that fills from the left.

   Deliberately without transition or animation. A bar is a property of the
   value, not an event — nothing here should draw the eye on a re-render.

   --gg-bar is set per cell; --gg-bar-color is set per cell only when the
   caller named one, so a skin can own the hue by redefining it here. */
.gg-bar {
  display: block;
  --gg-bar: 0%;
  --gg-bar-color: #3b82f6;
  border-radius: 2px;
  /* The trough is what makes a zero legible. Without it a row worth nothing
     and a row that was never measured (NULL, non-numeric — no bar at all)
     look identical, and the column quietly stops distinguishing "none" from
     "unknown". Kept far fainter than the fill so it reads as a track rather
     than as a value. */
  background-color: color-mix(in srgb, var(--gg-bar-color) 7%, transparent);
  background-image: linear-gradient(90deg,
      color-mix(in srgb, var(--gg-bar-color) 16%, transparent) 0,
      color-mix(in srgb, var(--gg-bar-color) 40%, transparent) var(--gg-bar),
      transparent var(--gg-bar));
}

/* Belt and braces: the totals row is painted by a different code path that
   never wraps a cell in .gg-bar, but a total has no scale to be measured
   against, so a stray bar there must not be able to appear. */
.gg-td-summary .gg-bar { background: none; }

/* IconWhen trailing icon — amber warning default, override per skin. */
.gg-icon-when { color: #d97706; }
.gg-icon-when svg { width: 0.875rem; height: 0.875rem; display: inline-block; vertical-align: -2px; margin-left: 4px; flex-shrink: 0; }

.gg-act-btn svg,
.gg-bulk-btn svg { width: 0.75rem; height: 0.75rem; }
.gg-act-bar-btn svg,
.gg-act-bar .gg-bulk-btn svg { width: 0.875rem; height: 0.875rem; flex-shrink: 0; }

.gg-act-icon { width: 0.75rem; height: 0.75rem; vertical-align: middle; }

.gg-sel-th {
  width: 2rem;
  padding: 0.5rem 0.5rem;
  text-align: center;
  vertical-align: middle;
}

.gg-sel-td {
  text-align: center;
  padding: 0.375rem 0.5rem;
  vertical-align: middle;
}

.gg-sel-cb {
  cursor: pointer;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  vertical-align: middle;
  accent-color: #3b82f6;
}

/* RecordView modal — opt-in via Grid.RecordView(true). */
.gg-recview-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(17, 24, 39, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.gg-recview-backdrop[hidden] { display: none; }

.gg-recview {
  background: #fff;
  color: #1f2937;
  border-radius: 0.5rem;
  box-shadow: 0 20px 25px -5px rgba(0,0,0,0.15), 0 10px 10px -5px rgba(0,0,0,0.08);
  width: min(640px, 100%);
  max-height: min(80vh, 720px);
  display: flex;
  flex-direction: column;
  font-size: 0.875rem;
}

.gg-recview-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.625rem 0.875rem;
  border-bottom: 1px solid #e5e7eb;
  background: #f3f4f6;
  border-top-left-radius: 0.5rem;
  border-top-right-radius: 0.5rem;
}
.gg-recview-title { font-weight: 600; font-size: 0.8125rem; color: #374151; }
.gg-recview-pos { font-variant-numeric: tabular-nums; margin-left: 0.25rem; color: #6b7280; font-weight: 500; }

.gg-recview-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; padding: 0;
  border: none; background: transparent; border-radius: 0.25rem;
  cursor: pointer; color: #6b7280;
}
.gg-recview-close:hover { background: #e5e7eb; color: #b91c1c; }
.gg-recview-close svg { width: 0.875rem; height: 0.875rem; }

/* Header tools: maximise (before) and close (after), same order as the host's
   own detail dialogs. */
.gg-recview-tools { display: inline-flex; align-items: center; gap: 0.25rem; }
.gg-recview-max {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; padding: 0;
  border: none; background: transparent; border-radius: 0.25rem;
  cursor: pointer; color: #6b7280;
}
.gg-recview-max:hover { background: #e5e7eb; color: #1f2937; }
.gg-recview-max svg { width: 0.875rem; height: 0.875rem; }
.gg-recview-ico-min { display: none; }
.gg-recview-backdrop.gg-recview-maxed { padding: 0; }
.gg-recview-backdrop.gg-recview-maxed .gg-recview {
  width: 100vw; height: 100vh; max-height: 100vh; border-radius: 0;
}
.gg-recview-backdrop.gg-recview-maxed .gg-recview-head { border-radius: 0; }
.gg-recview-backdrop.gg-recview-maxed .gg-recview-row {
  /* On a wide screen a fluid label column would push values to the middle. */
  grid-template-columns: minmax(8rem, 18rem) 1fr auto;
}
.gg-recview-backdrop.gg-recview-maxed .gg-recview-ico-max { display: none; }
.gg-recview-backdrop.gg-recview-maxed .gg-recview-ico-min { display: inline; }

.gg-recview-body {
  padding: 0.5rem 0.875rem;
  overflow-y: auto;
  flex: 1 1 auto;
}

.gg-recview-row {
  display: grid;
  grid-template-columns: minmax(8rem, 0.4fr) 1fr auto;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.375rem 0;
  border-bottom: 1px solid #f9fafb;
}
.gg-recview-row:last-child { border-bottom: none; }

.gg-recview-label {
  font-weight: 500;
  color: #6b7280;
  font-size: 0.75rem;
  /* Real casing — uppercase mangled XML camelCase field names. */
  /* Long, space-less labels (e.g. "NAZWA TOWARU/MATERIAŁU/OPAKOWANIA")
     otherwise overflow their grid track and spill onto the value column
     (bug.xml.pro #59). overflow-wrap:anywhere lets them break within the
     label column; min-width:0 lets the grid item shrink to it. */
  min-width: 0;
  overflow-wrap: anywhere;
}
.gg-recview-value {
  color: #1f2937;
  word-break: break-word;
  white-space: pre-wrap;
}

.gg-recview-copy {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; padding: 0;
  border: none; background: transparent; border-radius: 0.25rem;
  cursor: pointer; color: #6b7280;
  flex-shrink: 0;
}
.gg-recview-copy:hover { background: #e5e7eb; color: #1f2937; }
.gg-recview-copy svg { width: 0.75rem; height: 0.75rem; }

.gg-recview-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  border-top: 1px solid #e5e7eb;
  background: #f9fafb;
  border-bottom-left-radius: 0.5rem;
  border-bottom-right-radius: 0.5rem;
}

.gg-recview-nav { display: inline-flex; gap: 0.125rem; }
.gg-recview-nav button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem; padding: 0;
  border: 1px solid #d1d5db; background: #fff; border-radius: 0.25rem;
  cursor: pointer; color: #4b5563;
}
.gg-recview-nav button:hover:not(:disabled) { background: #f3f4f6; color: #1f2937; }
.gg-recview-nav button:disabled { opacity: 0.35; cursor: not-allowed; }
.gg-recview-nav svg { width: 0.875rem; height: 0.875rem; }

.gg-recview-copy-all {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0.25rem 0.625rem;
  font-size: 0.75rem;
  font-family: inherit;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  color: #4b5563;
  background: #fff;
  border: 1px solid #d1d5db;
  border-radius: 0.25rem;
  cursor: pointer;
}
.gg-recview-copy-all:hover { background: #f3f4f6; color: #1f2937; border-color: #9ca3af; }
.gg-recview-copy-all svg { width: 0.75rem; height: 0.75rem; }

/* Action columns of the row, mirrored into the footer (see rvActions). They
   sit first because they are what the reader came to do with this record;
   navigation and "copy all" are what they do to the panel. */
.gg-recview-actions { display: inline-flex; gap: 0.25rem; margin-right: auto; }
.gg-recview-action {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.75rem; font-family: inherit;
  color: #4b5563; background: #fff;
  border: 1px solid #d1d5db; border-radius: 0.25rem;
  cursor: pointer;
}
.gg-recview-action:hover { background: #f3f4f6; color: #1f2937; border-color: #9ca3af; }
.gg-recview-action svg { width: 0.875rem; height: 0.875rem; }

/* Dark-mode overrides (DaisyUI sets data-theme="dark" on <html>).
   Scoped to elements that sit OUTSIDE the table card so the grid
   surface itself stays on its own light background, but the
   toolbar export menu + dropdown don't look like white blobs on a
   dark page. */
[data-theme="dark"] .gg-export-menu > summary { color: #9ca3af; }
[data-theme="dark"] .gg-export-menu > summary:hover {
  background: #374151;
  color: #f3f4f6;
}
[data-theme="dark"] .gg-export-list {
  background: #1d232a;                    /* dark base-100 (daisyUI default) */
  border-color: #2a323c;                  /* dark base-200 */
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4),
              0 2px 4px -2px rgba(0, 0, 0, 0.4);
}
[data-theme="dark"] .gg-export-item { color: #a6adba; }
[data-theme="dark"] .gg-export-item:hover { background: #2a323c; color: #f3f4f6; }
[data-theme="dark"] .gg-pager-title { color: #d1d5db; }
[data-theme="dark"] .gg-page-link { border-color: #2a323c; }
[data-theme="dark"] .gg-page-link:hover { background: #2a323c; border-color: #3a424c; }
[data-theme="dark"] .gg-page-disabled { border-color: #23282e; }
[data-theme="dark"] .gg-collapse-toggle { color: #9ca3af; }
[data-theme="dark"] .gg-collapse-toggle:hover { background: #2a323c; color: #f3f4f6; }

/* Boolean cells (Column.BooleanAsCheckbox): the tick sits on the text
   baseline like a glyph; the label lives in a visually hidden span so the
   cell still reads "TAK" to click-to-copy, the record view and a screen
   reader. */
.gg-bool { display: inline-flex; align-items: center; vertical-align: middle; }
.gg-bool svg { width: 1rem; height: 1rem; }
/* Yes is a solid square with a white tick, no a faint outline — an outline
   with and without a small tick read the same from arm's length. Lucide's
   square-check is a <rect> plus the tick <path>, hence the two selectors.
   currentColor, so a dark theme needs no separate rule. */
.gg-bool-on svg rect { fill: currentColor; }
.gg-bool-on svg path { stroke: #fff; stroke-width: 2.5; }
.gg-bool-off { opacity: 0.45; }
.gg-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
