.sticky {
    position: sticky;
    z-index: 10;
}

.sticky:dir(ltr) {
    left: var(--sticky-offset);
}
.sticky:dir(rtl) {
    right: var(--sticky-offset);
}

/* stickyColumnsOffset (store.ts) leaves a deliberate 1px gap between sticky columns; this
   pseudo-element hides the scrolled row content that would otherwise show through it.
   `background-color: inherit` takes the cell's own current background, so it stays correct
   through row hover/theme changes instead of matching only the default white. The button
   column is excluded from the colored override below — it draws its own visible divider
   instead (Row.svelte, `border-inline-end`). */
.sticky::before {
    content: '';
    position: absolute;
    top: 0;
    height: 100%;
    width: 1px;
    background-color: inherit;
}
.sticky:dir(ltr)::before {
    right: -1px;
}
.sticky:dir(rtl)::before {
    left: -1px;
}
.sticky.isLastSticky.isHorizontallyScrolled:not(.button-cell)::before {
    background-color: var(--border-color);
}

/* applies shadow only on the left to avoid eating borders */
.isHorizontallyScrolled.isLastSticky::after {
    content: '';
    position: absolute;
    top: 0;
    height: 100%;
    width: 6px;
}
.isHorizontallyScrolled.isLastSticky:dir(ltr)::after {
    right: -6px;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0.13), transparent);
}
.isHorizontallyScrolled.isLastSticky:dir(rtl)::after {
    left: -6px;
    background: linear-gradient(-90deg, rgba(0, 0, 0, 0.13), transparent);
}

/* The whole row is pinned as one layer, so the shadow stays a single stable line:
a per-cell shadow would break apart as non-sticky columns scroll under sticky ones. */
.sticky-header {
    position: sticky;
    top: 0;
    z-index: 20;
}

.sticky-header.isVerticallyScrolled {
    box-shadow: 0 6px 6px -6px rgba(0, 0, 0, 0.13);
}
