/*
    Styling for the ProductAuthorshipTableGrid component, which builds on the sitewide "table"
    CSS class (Table.css): the frame around its scroll container, the table and its cells, the
    summary, body, empty and spacer rows, their motion, the column drag image, and the custom
    properties the header cells share with the rest of the table. The header cells, which the
    ProductAuthorshipTableGridHeaderCell component renders, have a stylesheet of their own. Row
    windowing (rendering only the rows near the view) and frozen columns need every row to be
    exactly the row height and every column exactly its declared width. The rules repeat the "table"
    CSS class so they win over Table.css whatever the stylesheet order.

    A page can set these custom properties on an element around the table to tune it; each falls
    back to the table's own look while unset:
    - --sortable-table-frozen-columns-line-color: the line between the frozen and scrolled columns.
    - --sortable-table-slanted-header-icon-band-added-height: extra height for the band that holds
      slanted headers' icons, for a header control that reaches below the icons.

    A header control in a slanted header can read --sortable-table-slanted-header-icon-size, the size
    of the icons there, to match them. A frozen cell's content can read
    --sortable-table-frozen-cell-left, how far the cell is from the table's left edge, to reach back
    over the frozen cells before it, as a click area filling a summary row's frozen columns does.
*/

/* The opacity of the body cells' content, which the body animates after a new sort and the cells
   inherit. Registering it as a number lets the browser animate it; an unregistered custom property
   is text, which would jump from one value to the next. */
@property --sortable-table-sorted-content-opacity
{
    syntax: "<number>";
    inherits: true;
    initial-value: 1;
}

/* Styling for the frame around the scroll container, which holds the view's edge shadows. */
.sortable-table-edge-shadow-frame
{
    /* The space on each side of a cell's content. The trailing side is wide enough to also hold the
       sort arrow, which sits beside a header's label; every column reserves it, so the arrow
       appearing never widens a column. */
    --sortable-table-cell-leading-padding: 0.5rem;
    --sortable-table-cell-trailing-padding: 1rem;
    /* A column's width beyond its content: the leading padding plus the trailing padding. Each
       frozen column is offset by this amount per preceding frozen column, so it must cover all of a
       cell's horizontal space. */
    --sortable-table-cell-horizontal-padding-total: calc(var(--sortable-table-cell-leading-padding) + var(--sortable-table-cell-trailing-padding));
    /* The same for a column with a slanted header, whose sort arrow sits in the header's corner
       rather than beside its label, so its cells need no room for the arrow and stay narrow. The
       padding is the same on both sides, so the total is twice it. */
    --sortable-table-slanted-column-cell-padding: 0.25rem;
    --sortable-table-slanted-column-cell-horizontal-padding-total: calc(2 * var(--sortable-table-slanted-column-cell-padding));
    /* The sort arrow's width, and the gap between it and the label. Together they are the room a
       header takes from its trailing padding for the arrow, so they must not exceed it. */
    --sortable-table-sort-arrow-width: 0.5rem;
    --sortable-table-sort-arrow-gap: 0.25rem;
    --sortable-table-sort-arrow-space: calc(var(--sortable-table-sort-arrow-width) + var(--sortable-table-sort-arrow-gap));
    /* The sort arrow's height, a little taller than the label's capital letters, so its thin head
       reads as an arrow's. */
    --sortable-table-sort-arrow-height: 0.85em;
    --sortable-table-header-vertical-padding: 0.5rem;
    /* A header's trailing padding is the cells' less the sort arrow's room. */
    --sortable-table-header-trailing-padding: calc(var(--sortable-table-cell-trailing-padding) - var(--sortable-table-sort-arrow-space));
    /* The band a header keeps free above its label for a header control, and the control's inset
       from the header's top-right corner. */
    --sortable-table-header-control-height: 1em;
    --sortable-table-header-control-inset: 2px;
    --sortable-table-row-line-width: 1px;
    /* The stacking levels in the header, lowest first, in two separate scales. Inside a header cell,
       which stacks its own parts: its slanted cell, then its label, sort arrow, subline and control,
       then the control on the line under the header row, then a subline with an open list. Among the
       header row's cells and the summary cells under them: the frozen headers, which the slanted
       cells pass under, then the cells holding the control on the line under the header row, then a
       header control with an open list, then a header whose subline has one. A number may repeat
       between the scales, since they never compete. */
    --sortable-table-slanted-cell-z-index: 1;
    --sortable-table-header-part-z-index: 2;
    --sortable-table-edge-control-z-index: 3;
    --sortable-table-open-list-part-z-index: 4;
    --sortable-table-frozen-header-z-index: 3;
    --sortable-table-edge-control-holder-z-index: 4;
    --sortable-table-open-list-control-z-index: 5;
    --sortable-table-open-list-header-z-index: 6;
    --sortable-table-frozen-columns-separator-width: 1px;
    /* The edge shadows match ScrollEdgeShadowFrame.css's, so the site's scroll shadows look alike. */
    --sortable-table-edge-shadow-width: 0.75rem;
    --sortable-table-edge-shadow-fade-duration: 200ms;
    /* The rows' content fades in from this opacity after a new sort. */
    --sortable-table-sort-fade-start-opacity: 0.6;
    --sortable-table-sort-fade-duration: 200ms;
    /* A hovered row brightens over the same time as in Table.css. */
    --sortable-table-row-hover-fade-duration: 0.3s;
    --sortable-table-drop-bar-width: 3px;

    /* The frame takes the place of the scroll container in its parent's layout, so it fills the
       parent's height the way Table.css's ".table-container" does. */
    position: relative;
    min-height: 0;
    height: 100%;
    /* The edge shadows' stacking order stays within the frame. */
    isolation: isolate;

    /* The browser's scroll anchoring is off: the spacer rows already keep the rows in view in place
       as rows above them are rendered and removed, and an extra shift would move the view away from
       the rows just rendered for it. */
    > .table-container
    {
        overflow-anchor: none;
        /* The space past the last column and below the last row continues the header's and the
           rows' backgrounds, so a table narrower or shorter than its view still reads as one block.
           The header's band is as tall as the rows that stay at the top, set from the script. The
           gradient runs top to bottom in the header's color down to that height, then changes at
           once to the rows' color: both colors stop at the same height, so nothing blends. */
        background: linear-gradient(
            to bottom,
            var(--neutral-medium-emphasis-surface-color) var(--sortable-table-top-sticky-rows-height),
            var(--neutral-low-emphasis-surface-color) var(--sortable-table-top-sticky-rows-height));
    }

    /* The shadows at the view's left and right edges, shown while more columns lie beyond them.
       The frozen columns' width and the view's size, which excludes the scroll bars, are set from
       the script, so the shadows stay off the frozen columns and the scroll bars. */
    &::before,
    &::after
    {
        content: "";
        position: absolute;
        top: 0;
        /* Above the table, which is a stacking context of its own. */
        z-index: 1;
        width: var(--sortable-table-edge-shadow-width);
        height: var(--sortable-table-view-height);
        /* Black at 65% opacity. */
        --sortable-table-edge-shadow-color: rgba(0, 0, 0, 0.65);
        /* The shadows don't block clicks or drags on the cells beneath them. */
        pointer-events: none;
        /* Hidden while no columns lie beyond the shadow's edge; the classes below fade it in. */
        opacity: 0;
        transition: opacity var(--sortable-table-edge-shadow-fade-duration) ease;
    }
    &::before
    {
        left: var(--sortable-table-frozen-columns-width);
        background: linear-gradient(to right, var(--sortable-table-edge-shadow-color), transparent);
    }
    &::after
    {
        /* The right shadow's left edge sits one shadow width in from the view's right edge. */
        left: calc(var(--sortable-table-view-width) - var(--sortable-table-edge-shadow-width));
        background: linear-gradient(to left, var(--sortable-table-edge-shadow-color), transparent);
    }
    &.sortable-table-columns-beyond-left-edge::before,
    &.sortable-table-columns-beyond-right-edge::after
    {
        opacity: 1;
    }
}

.table.sortable-table
{
    /* Columns take their declared widths, rather than stretching to fill the container, so a long
       name never widens number-only columns. */
    width: auto;
    /* Separate borders keep each cell's border with the cell; collapsed borders stay behind when
       frozen cells and the sticky header move over the scrolled cells. */
    border-collapse: separate;
    border-spacing: 0;

    /* Styling for every header, body, and summary cell. */
    th,
    td
    {
        padding-left: var(--sortable-table-cell-leading-padding);
        padding-right: var(--sortable-table-cell-trailing-padding);

        &.sortable-table-left-aligned-cell
        {
            text-align: left;
        }
        /* A centered column's cells split the padding evenly, so their content sits on the cell's
           center; the column stays as wide as with the usual padding. */
        &.sortable-table-center-aligned-cell
        {
            text-align: center;
            padding-left: calc(var(--sortable-table-cell-horizontal-padding-total) / 2);
            padding-right: calc(var(--sortable-table-cell-horizontal-padding-total) / 2);
        }
        &.sortable-table-right-aligned-cell
        {
            text-align: right;
        }

        /* Styling for the cells of frozen columns, which stay in place while the other columns
           scroll sideways. Each one's left offset is set from the script. */
        &.sortable-table-frozen-cell
        {
            position: sticky;
            /* Above the scrolled cells in the same header, body, or footer. */
            z-index: var(--above-table-body-z-index);
        }

        /* A line at the right edge of the frozen columns separates them from the scrolled columns. */
        &.sortable-table-last-frozen-cell
        {
            border-right: var(--sortable-table-frozen-columns-separator-width) solid var(--sortable-table-frozen-columns-line-color, var(--SUBTLE-CELL-SEPARATION-BORDER-COLOR));
        }
    }

    /* The control on the line under the header row and any summary rows under it, held by a cell
       of the last of those rows: centered on the last frozen cell's right edge, where that line meets
       the line between the frozen and scrolled columns, or under the first cell's middle when no
       column is frozen. A summary cell holding it sits over the cells beside it and the rows below. */
    thead td.sortable-table-cell-with-edge-control
    {
        z-index: var(--sortable-table-edge-control-holder-z-index);
    }
    thead td.sortable-table-cell-with-edge-control:not(.sortable-table-frozen-cell)
    {
        position: relative;
    }
    thead .sortable-table-header-edge-control
    {
        position: absolute;
        /* Above its header's slanted cell and label, so a hovered header's brighter cell never
           covers it. */
        z-index: var(--sortable-table-edge-control-z-index);
        bottom: 0;
        left: 50%;
        /* Moved left and down by half its own width and height, so its center sits on the point
           that its bottom-left corner was placed at. */
        transform: translate(-50%, 50%);
        line-height: 0;
        cursor: default;
    }
    thead .sortable-table-last-frozen-cell > .sortable-table-header-edge-control
    {
        left: 100%;
    }

    /* A column with a slanted header has narrower cell padding, the same on both sides. */
    th.sortable-table-slanted-column-cell,
    td.sortable-table-slanted-column-cell
    {
        padding-left: var(--sortable-table-slanted-column-cell-padding);
        padding-right: var(--sortable-table-slanted-column-cell-padding);
    }

    /* Styling for the body and summary cells. Summary rows shown under the header row are in the
       header. */
    tbody td,
    thead td,
    tfoot td
    {
        /* Each row's height is its content box alone, matching the row height the spacer rows are
           sized by. The line under each row is an inner shadow rather than a border, since
           browsers round a border's width to whole screen pixels: at display scales such as 125%,
           rows would come out shorter than the spacer rows assume and shift on each scroll. */
        padding-top: 0;
        padding-bottom: 0;
        border-top: none;
        border-bottom: none;
        /* The inner shadow's horizontal offset and blur are 0 and its vertical offset is the line's
           width, negative so the line is at the cell's bottom. */
        box-shadow: inset 0 calc(-1 * var(--sortable-table-row-line-width)) 0 var(--SUBTLE-CELL-SEPARATION-BORDER-COLOR);
    }

    /* Summary rows under the header row have the footer's background, as in Table.css. */
    thead td
    {
        background: var(--neutral-medium-emphasis-surface-color);
    }

    /* Top summary rows that can be hidden: while none show, the line under the header row is
       double across the whole table, as spreadsheets mark rows that can be shown. While they show,
       the lines above and below them are thick instead, setting them apart from the header row and
       the rows below. The header row's line is a border; the last summary row's is an inner shadow,
       which keeps the row at exactly the row height. The widths are visual choices and can be tuned. */
    &.sortable-table-with-hideable-top-summary-rows thead
    {
        --sortable-table-hideable-summary-rows-line-color: var(--neutral-surface-low-emphasis-contrast-color);
        --sortable-table-hideable-summary-rows-thick-line-width: 1.5px;

        tr:only-child > th
        {
            border-bottom: 4px double var(--sortable-table-hideable-summary-rows-line-color);
        }
        tr:first-child:not(:only-child) > th
        {
            border-bottom: var(--sortable-table-hideable-summary-rows-thick-line-width) solid var(--sortable-table-hideable-summary-rows-line-color);
        }
        tr:last-child:not(:first-child) > td
        {
            /* As for every row's line: no horizontal offset or blur, and a negative vertical offset
               of the line's width, which puts the line at the cell's bottom. */
            box-shadow: inset 0 calc(-1 * var(--sortable-table-hideable-summary-rows-thick-line-width)) 0 var(--sortable-table-hideable-summary-rows-line-color);
        }
    }

    /* Top summary rows slide in and out: each row's cells grow from no height to the row's, or
       shrink back, so the rows below follow. */
    thead.sortable-table-summary-rows-sliding-in > tr:not(:first-child) .sortable-table-cell-content
    {
        animation: sortable_table_summary_row_slide_in var(--sortable-table-summary-row-slide-duration) ease-out;
    }
    thead.sortable-table-summary-rows-sliding-out > tr:not(:first-child) .sortable-table-cell-content
    {
        animation: sortable_table_summary_row_slide_out var(--sortable-table-summary-row-slide-duration) ease-in forwards;
    }

    /* Styling for the content box inside each body and summary cell. Its height keeps the row at
       exactly the row height, clipping content that doesn't fit on one line. */
    .sortable-table-cell-content
    {
        height: var(--sortable-table-row-height);
        line-height: var(--sortable-table-row-height);
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        /* The body animates this after a new sort; it is 1 otherwise, and in summary rows. */
        opacity: var(--sortable-table-sorted-content-opacity);
    }

    /* The content of a cell that shows its full text in a tooltip, inside the tooltip's trigger. The
       trigger is a block of its own, so the content box's ellipsis doesn't reach the text and this
       box cuts the text short instead. */
    .sortable-table-truncatable-cell-content
    {
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Content marked to overflow left, in a right-aligned cell, ends at the cell's right padding
       and runs past its left edge, over the empty cells beside it, rather than being cut short. */
    td.sortable-table-right-aligned-cell .sortable-table-cell-content:has(> .sortable-table-left-overflowing-content)
    {
        direction: rtl;
        overflow: visible;

        > .sortable-table-left-overflowing-content
        {
            direction: ltr;
            unicode-bidi: isolate;
        }
    }

    /* After a new sort, the body fades in its cells' content, alternating between two identical
       animations so that each new sort starts the fade again. */
    tbody.sortable-table-even-sort-fade
    {
        animation: sortable_table_sorted_content_fade_after_even_sort var(--sortable-table-sort-fade-duration) ease-out;
    }
    tbody.sortable-table-odd-sort-fade
    {
        animation: sortable_table_sorted_content_fade_after_odd_sort var(--sortable-table-sort-fade-duration) ease-out;
    }

    /* Styling for the body rows. */
    tbody tr
    {
        /* Table.css animates every change to a body row over 0.3s. Here, a row moved by a new sort
           would cross-fade into its new band for longer than the content's fade, so rows change
           at once, except for brightening on hover. */
        transition: none;

        /* Rows are banded by their position among all displayed rows, so a row keeps its band as
           rows scroll in and out. Table.css's banding would count the spacer rows. */
        &.sortable-table-even-row
        {
            background: var(--neutral-lower-emphasis-surface-color);
        }
        &.sortable-table-odd-row
        {
            background: var(--neutral-low-emphasis-surface-color);
        }
        /* Rows that aren't banded all take the odd rows' shade. */
        &.sortable-table-unbanded-row
        {
            background: var(--neutral-low-emphasis-surface-color);
        }
        &.sortable-table-even-row:hover,
        &.sortable-table-odd-row:hover,
        &.sortable-table-unbanded-row:hover
        {
            background: var(--neutral-medium-emphasis-surface-color);
            /* A change runs the transition of the state it changes to, so this brightens a hovered
               row gradually and returns it to its band at once. */
            transition: background-color var(--sortable-table-row-hover-fade-duration) ease;
        }

        /* A row that a new filter hides fades out before the rows move into its place; the
           script removes it once the fade's time is up. */
        &.sortable-table-leaving-row
        {
            animation: sortable_table_row_leave var(--sortable-table-row-leave-duration) ease-in forwards;
        }

        /* A dimmed row, as for something no longer current. The frozen cells keep their opaque
           background, which covers the cells scrolled beneath them, so only their content is
           dimmed; the other cells are dimmed whole, their own shading included. The opacity is a
           visual choice and can be tuned. */
        &.sortable-table-dimmed-row
        {
            --sortable-table-dimmed-row-opacity: 0.55;

            > td:not(.sortable-table-frozen-cell)
            {
                opacity: var(--sortable-table-dimmed-row-opacity);
            }
            > td.sortable-table-frozen-cell .sortable-table-cell-content
            {
                /* The dimmed opacity times the fade after a new sort, which this content box would
                   otherwise take alone. */
                opacity: calc(var(--sortable-table-dimmed-row-opacity) * var(--sortable-table-sorted-content-opacity));
            }
        }

        /* Frozen body cells take their row's background, covering the cells scrolled beneath them. */
        td.sortable-table-frozen-cell
        {
            background: inherit;
        }
    }

    /* Styling for the row that shows the empty message while no row is displayed: low-emphasis text,
       held at the view's left edge as the columns scroll, with no row line under it. */
    tbody tr.sortable-table-empty-row
    {
        background: none;

        td
        {
            padding: 0.75rem;
            box-shadow: none;
        }

        /* The message takes no width in the table's layout and runs past the last column on one
           line, so a message wider than the columns never stretches them, which would move them
           away from the widths the frozen columns' offsets assume. */
        .sortable-table-empty-message
        {
            position: sticky;
            left: 0.75rem;
            width: 0;
            white-space: nowrap;
            color: var(--neutral-surface-low-emphasis-contrast-color);
        }
    }

    /* Styling for the empty rows that stand in for the rows that aren't rendered. Their height is
       set on their one cell, so the cell adds no padding or line to it. */
    tbody tr.sortable-table-spacer-row
    {
        background: none;

        td
        {
            padding: 0;
            border: none;
            box-shadow: none;
        }
    }
}

/* Styling for the table when the user's system asks for less motion: the rows' content appears at
   once after a new sort, and rows leave and summary rows come and go at once. */
@media (prefers-reduced-motion: reduce)
{
    .table.sortable-table tbody.sortable-table-even-sort-fade,
    .table.sortable-table tbody.sortable-table-odd-sort-fade,
    .table.sortable-table tbody tr.sortable-table-leaving-row,
    .table.sortable-table thead.sortable-table-summary-rows-sliding-in > tr:not(:first-child) .sortable-table-cell-content,
    .table.sortable-table thead.sortable-table-summary-rows-sliding-out > tr:not(:first-child) .sortable-table-cell-content
    {
        animation: none;
    }
}

/* The fade of a row that a new filter hides. */
@keyframes sortable_table_row_leave
{
    to
    {
        opacity: 0;
    }
}

/* The slides of top summary rows' cells as the rows are shown and hidden. */
@keyframes sortable_table_summary_row_slide_in
{
    from
    {
        height: 0;
    }
}

@keyframes sortable_table_summary_row_slide_out
{
    to
    {
        height: 0;
    }
}

/* The fades of the rows' cell content after a new sort, one for each parity of the number of sorts.
   They are identical; switching between them starts the fade again. */
@keyframes sortable_table_sorted_content_fade_after_even_sort
{
    from
    {
        --sortable-table-sorted-content-opacity: var(--sortable-table-sort-fade-start-opacity);
    }
    to
    {
        --sortable-table-sorted-content-opacity: 1;
    }
}
@keyframes sortable_table_sorted_content_fade_after_odd_sort
{
    from
    {
        --sortable-table-sorted-content-opacity: var(--sortable-table-sort-fade-start-opacity);
    }
    to
    {
        --sortable-table-sorted-content-opacity: 1;
    }
}

/* Styling for a table with slanted headers, which let narrow columns show long labels. Each slanted
   header's cell leans 45 degrees to the right, reaching over the headers to its right by the header
   row's height, and its label rises along it. */
.table.sortable-table.sortable-table-with-slanted-headers
{
    /* The icons at the top of a slanted header, in its header control: squares this
       size, half again the font's size so they are easy to hit, with this gap around them. */
    --sortable-table-slanted-header-icon-size: 1.5em;
    --sortable-table-slanted-header-icon-gap: 2px;
    /* The band at the top of a slanted header that holds its icons, above its label's far end: an
       icon's size, the gap above and below it, and any added height a page sets. */
    --sortable-table-slanted-header-icon-band-height: calc(
        var(--sortable-table-slanted-header-icon-size) + 2 * var(--sortable-table-slanted-header-icon-gap) +
        var(--sortable-table-slanted-header-icon-band-added-height, 0px));
    /* How far up a label starts from the header's bottom edge, and the space between a label and its
       cell's left edge; visual choices. */
    --sortable-table-slanted-label-start-height: 0.25rem;
    --sortable-table-slanted-label-edge-gap: 0.25rem;
    /* How far the upright sort arrow sits in from the header's right edge; a visual choice, the
       same as the label's starting height today, but free to change apart from it. */
    --sortable-table-slanted-sort-arrow-right-inset: 0.25rem;
    /* The width of the line between slanted header cells: the row lines' width today, a visual
       choice that can change apart from them. */
    --sortable-table-slanted-column-line-width: 1px;
    /* The room for a label, set from the script: the widest label's bold form, up to a maximum. */
    /* The header's height inside its bottom line, which is how far right its cell's top leans: the
       label's room and the label's line, turned 45 degrees (so times the sine of 45 degrees), above
       where the label starts, with the icons' band on top. The label and the icons are placed from
       the header's edges, not its padding, so the padding adds no height; the longest label then
       starts just below the icons. */
    --sortable-table-slanted-header-height: calc(
        (var(--sortable-table-slanted-label-room) + 1lh) * 0.7071 +
        var(--sortable-table-slanted-label-start-height) +
        var(--sortable-table-slanted-header-icon-band-height));
    /* The height of a header's content, inside its padding: the header's height less its top and
       bottom padding. */
    --sortable-table-slanted-header-content-height: calc(var(--sortable-table-slanted-header-height) - 2 * var(--sortable-table-header-vertical-padding));
    /* Room past the last column for its slanted cell, which reaches beyond the table's right edge. */
    margin-right: var(--sortable-table-slanted-header-height);
}

/* The picture under the pointer while a column is dragged: its label in a small box, drawn off
   screen for the browser to take its picture of. */
.sortable-table-drag-image
{
    position: fixed;
    top: -1000px;
    left: 0;
    padding-top: 0.25rem;
    padding-right: 0.5rem;
    padding-bottom: 0.25rem;
    padding-left: 0.5rem;
    border: 1px solid var(--neutral-higher-emphasis-surface-color);
    border-radius: 4px;
    color: var(--neutral-surface-highest-emphasis-contrast-color);
    background: var(--neutral-high-emphasis-surface-color);
    white-space: nowrap;
}

/* A full-text tooltip for a cell that was last measured as fitting, so it would only repeat the
   text. The tooltip is rendered in the page's tooltip container, outside the table. Hiding it,
   rather than removing it from the layout, keeps its size measurable for placing it. */
.tooltip.sortable-table-untruncated-cell-tooltip
{
    visibility: hidden;
}
