/*
 * Atlassian's design tokens, with a fallback beside each one.
 *
 * On a dashboard the tokens come from the page the gadget is embedded in, and
 * they are what makes a Custom UI gadget agree with the dashboard around it in
 * both themes. The fallbacks are for everywhere else: `npm run dev`, the built
 * bundle opened straight in a browser, and the screenshots taken from it.
 *
 * The fallbacks answer `prefers-color-scheme` rather than being one fixed
 * pair. Fixed, they were dark navy text on the dark background this browser
 * defaults to, and the heading was unreadable in the first screenshot taken of
 * it — a defect that lives entirely outside the product and shows up in every
 * picture anybody is shown of it.
 */
.figure-gadget {
  color-scheme: light dark;

  --fallback-surface: #ffffff;
  --fallback-text: #172b4d;
  --fallback-text-subtlest: #626f86;
  /* One step darker, for text that sits on a coloured fill. The subtlest above
     is measured against the surface and passes there at 5.08:1; on the deepest
     cells of the scale it is 3.79:1, under the 4.5:1 floor. Measured for this
     one, over all eight pairs, 5.72:1 in light and 4.55:1 in dark at worst.
     The 4.83:1 this used to quote is a figure neither theme produces. */
  --fallback-text-subtle: #44546f;
  /* Low-alpha overlays are the fallback that keeps catching me out: a token
     meant to sit on a surface the host provides disappears against the other
     theme's background. These two are opaque on purpose. */
  --fallback-neutral: #091e420f;
  --fallback-border: #091e4224;
  --fallback-warning: #a54800;
  --fallback-selected: #e9f2fe;
  --fallback-selected-text: #05407f;
  /* The focus ring, which lands on more grounds than any other colour here:
     the card, the four fills of the colour scale, and the frozen columns. It
     was written as a literal at each of its four call sites and so was the one
     fallback the dark block below could not redefine - measured at 2.40:1 on
     the deepest fill in the dark palette, against the 3:1 WCAG 2.4.11 asks of
     a focus indicator. Light passes on every ground at 3.88 and up. */
  --fallback-focus: #1868db;
  /* A control that is refusing, which this product deliberately leaves in the
     tab order and answering to a press - so the WCAG 1.4.3 exception for an
     inactive component does not apply to it, and it needs 4.5:1 like any other
     text. The disabled token's own fallback is #8590a2, measured at 3.23:1 on
     white; this is 4.52. Two values rather than one, because no single grey
     clears the floor on both grounds. */
  --fallback-disabled: #6b778c;
  /* The three grounds Jira paints a status on, and the ink each one takes.
     These are the product's own lozenge colours rather than a guess at them, so
     a status in this gadget and the same status on the board beside it are the
     same colour. Measured on their own grounds: 8.59:1 for the neutral ink,
     8.33:1 for the information one and 6.30:1 for the success one, all clear of
     the 4.5 floor this file holds every other pair to. */
  --fallback-category-neutral: #dcdfe4;
  --fallback-category-neutral-text: #42526e;
  --fallback-category-progress: #e9f2fe;
  --fallback-category-progress-text: #0055cc;
  --fallback-category-done: #dcfff1;
  --fallback-category-done-text: #216e4e;

  /* The colours one series of the chart is drawn in.
   *
   * Okabe and Ito's palette, published for exactly this: a set that stays
   * apart under the three common kinds of colour blindness. Their black is
   * left out, because a black bar on a dark dashboard is not a bar, so seven
   * remain - an eighth column starts the list again, which is a wrap a legend
   * can still be read against because the names beside the swatches differ.
   *
   * Declared here as well as in `ui/Chart.jsx` for the same reason every
   * colour on this page is declared twice: the file that draws has to answer
   * when there is no stylesheet at all, and the page has to be able to change
   * its mind without a rebuild. The same seven, and a test holds them equal.
   *
   * The same in both themes. These are fills with ink beside them rather than
   * on them, so there is no pair here to measure a contrast between - what a
   * dark theme changes is the ink and the rules, which are the two below. */
  --crosstab-series-1: #e69f00;
  --crosstab-series-2: #56b4e9;
  --crosstab-series-3: #009e73;
  --crosstab-series-4: #f0e442;
  --crosstab-series-5: #0072b2;
  --crosstab-series-6: #d55e00;
  --crosstab-series-7: #cc79a7;
  /* The one segment of a bar that is not a column, where a grid has more
     columns than the legend can hold. Grey on purpose: a seventh colour would
     read as one more status, and the legend saying otherwise is the thing a
     reader glancing at a chart does not read. Named rather than numbered,
     because a page overriding the seven above is choosing a palette and this is
     not part of one. */
  --crosstab-series-rest: #8590a2;
  /* The axis labels and the lines behind the bars, so the chart is drawn in
     the page's own ink rather than in the library's default grey. */
  --crosstab-chart-ink: var(--ds-text, var(--fallback-text));
  --crosstab-chart-rule: var(--ds-border, var(--fallback-border));
}

@media (prefers-color-scheme: dark) {
.figure-gadget {
    --fallback-surface: #1f1f21;
    --fallback-text: #c7d1db;
    --fallback-text-subtlest: #8c9bab;
    --fallback-text-subtle: #9fadbc;
    --fallback-neutral: #2c2c2e;
    --fallback-border: #3c3c3f;
    --fallback-warning: #f5a623;
    --fallback-selected: #1c2b42;
    --fallback-selected-text: #9dc3f7;
    /* Measured against the two deepest fills of the scale, which are the worst
       grounds it can land on: 3.80:1 on the rising one and 4.58:1 on the
       falling, and 6.01:1 on the card. A ring is not text and its floor is 3. */
    --fallback-focus: #579dff;
    /* 5.10:1 on the dark card, where the light value measures 3.64. */
    --fallback-disabled: #8590a2;
    /* The same three, as Jira paints them on a dark board. Measured on their
       own grounds: 8.66:1, 7.93:1 and 7.44:1. */
    --fallback-category-neutral: #2c333a;
    --fallback-category-neutral-text: #b6c2cf;
    --fallback-category-progress: #1c2b41;
    --fallback-category-progress-text: #85b8ff;
    --fallback-category-done: #1c3329;
    --fallback-category-done-text: #7ee2b8;
  }
}

/* The size as well as the family.
 *
 * Nothing here set a size, so the gadget rendered at the browser default of
 * 16px while Atlassian's body text is 14. That is not only a mismatch with
 * everything around it: column width is the binding constraint on this product,
 * and it is set by the header, so two points cost about 12% of every column.
 *
 * `--ds-font-body` is a composite shorthand and carries the family with it,
 * which is why this replaces the family declaration rather than sitting beside
 * one. Confirmed to exist in the published token set rather than assumed; the
 * fallback is what `npm run dev` and the preview page get, since the tokens are
 * only defined inside a gadget. */
.figure-gadget {
  margin: 0;
  font: var(
    --ds-font-body,
    normal 400 14px / 20px ui-sans-serif,
    -apple-system,
    'Segoe UI',
    Roboto,
    sans-serif
  );
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface, var(--fallback-surface));
}

.gadget {
  padding: var(--ds-space-200, 16px);
  /* So the cells can ask how wide the gadget is. Jira already lets a reader
     choose a one, two or three column dashboard, which makes the width a lever
     they have and the stylesheet used to ignore. `inline-size` changes no
     layout of its own. */
  container-type: inline-size;
  container-name: gadget;
}

/* The name of a card that is not a table. A heading rather than a bold
   paragraph, so a reader can jump to it - and sized to the text around it,
   because a browser's own h3 is larger than anything else in a gadget. */
.lead {
  margin: 0;
  font-size: inherit;
  font-weight: 600;
}

.hint {
  margin: var(--ds-space-050, 4px) 0 0;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* The states that are not a grid. */
.note-warn .hint {
  color: var(--ds-text-warning, var(--fallback-warning));
}

/* The way out of a state that only says what went wrong.
 *
 * Under the sentence rather than beside it, for the reason `.setup-moved`
 * gives: these sentences wrap to three lines in a dashboard column, and a
 * button on the end of a wrapping paragraph lands somewhere different every
 * time the wording changes.
 *
 * Drawn as a plain bordered control rather than the brand fill the form's
 * primary button carries. What it offers is a second attempt at something
 * that failed, not the action of the card. */
.ask-again {
  font: inherit;
  margin-top: var(--ds-space-100, 8px);
  padding: var(--ds-space-075, 6px) var(--ds-space-150, 12px);
  border: 1px solid var(--ds-border, var(--fallback-border));
  border-radius: var(--ds-border-radius, 3px);
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-background-neutral-subtle, transparent);
  cursor: pointer;
}

/* The press that changes how much of the grid is drawn.
 *
 * Inside the sentence rather than under it: the line already says what is
 * being held back, and a button on a line of its own would cost a card in a
 * dashboard column the one thing it has least of. Drawn as the text around it
 * with an underline, which is what everything else in this product that leads
 * somewhere looks like. */
.drawn .limit {
  font: inherit;
  border: 0;
  padding: 0;
  background: none;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* The configuration form. Four inputs, no component library: it would be most
   of the bundle. */
.form {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-200, 16px);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-050, 4px);
}

/* The name of a field, which is what this weight is for.
 *
 * Not the label of a checkbox. Three of the four checkboxes in this form sit
 * inside a `.field`, so this already made them bold - and bold is the channel
 * the ticked state was using to say it was on. Measured in a browser, `Story
 * point estimate`, `Epics` and `Sub-tasks` computed identically before and
 * after the click, pixel for pixel: the weight said nothing because it never
 * changed, and the colour said nothing for the reason below. */
.field label:not(.mode) {
  font-weight: 600;
}

/* The mark on a control Save cannot go without, coloured the way Atlassian's
 * own forms colour it. The words above the first one carry it for a reader who
 * cannot tell the colour, and the control says "required" to a screen reader. */
.required {
  margin-inline-start: 2px;
  color: var(--ds-text-danger, var(--fallback-warning));
}

.field input {
  font: inherit;
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-raised, var(--fallback-surface));
  border: 1px solid var(--ds-border-input, #8590a2);
  border-radius: var(--ds-border-radius, 3px);
  padding: var(--ds-space-075, 6px) var(--ds-space-100, 8px);
}

.field input:disabled {
  color: var(--ds-text-disabled, var(--fallback-disabled));
}

.hint.warn {
  color: var(--ds-text-warning, var(--fallback-warning));
}

/* The name of a group, which was drawn in exactly the type of a paragraph.
 *
 * Measured: `legend` and `p.hint` computed identically - rgb(140, 155, 171),
 * 14px, weight 400 - so the heading of a group was lighter on the screen than
 * the label of a control inside it, and the structure that exists in the
 * markup existed nowhere a reader could see. The subtle token rather than the
 * subtlest, which is 7.4:1 in dark and 8.6:1 in light. */
.form legend {
  font-weight: 600;
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
}

/* Where the four required choices end and the rest begins.
 *
 * A line of type rather than a rule. A horizontal rule in a column 346 pixels
 * wide reads as the end of the form, and a reader who stops there has stopped
 * one choice short of the button. The space above it is what separates; the
 * words are what say which side is which. */
.form .hint.rest-optional {
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
  margin-top: var(--ds-space-100, 8px);
  padding-top: var(--ds-space-100, 8px);
  border-top: 1px solid var(--ds-border, var(--fallback-border));
}

/* The one line in this form that is about the state the form is in right now.
 *
 * Six explanations stand between the last picker and it, every one of them a
 * `hint` in the subtlest grey, and this used to be a seventh. The reader who
 * reported it had chosen three of the four, sat in front of a grey Save, and
 * read straight past the sentence saying which choice was missing - not
 * because it was far away, it is directly above the button, but because
 * nothing about it said it was different from the advice around it.
 *
 * Full contrast rather than the warning colour. Nothing has gone wrong: a form
 * that is not finished yet is not an error, and the orange is spoken for by
 * the two refusals that are. */
.hint.waiting {
  color: var(--ds-text, var(--fallback-text));
}

/* The controls that sentence names, which are controls rather than words.
 *
 * Drawn as text rather than as a button, because a row of buttons inside a
 * sentence is a sentence nobody reads as one. What makes it a control is the
 * underline and the cursor, which is the convention every reader already has
 * for words that take you somewhere. */
.hint.waiting .goto {
  font: inherit;
  color: inherit;
  padding: 0;
  border: none;
  background: none;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* And the mark beside each of those controls, so the reader who is standing at
 * the picker does not have to have read the sentence at the foot of the form.
 *
 * Warning-coloured, where the sentence is not: this is next to the control it
 * is about, so there is no register to distinguish it from - and against a
 * label in 600 weight the subtlest grey reads as a caption rather than as a
 * state. The words carry it for anybody who cannot tell the two apart. */
.field .hint.outstanding {
  color: var(--ds-text-warning, var(--fallback-warning));
}

/* The control itself, marked. Colour alone says nothing to a reader who cannot
 * see it and nothing to one who cannot tell these two greys apart, which is
 * why the line above exists - this is the part that finds the picker for
 * somebody scrolling past at speed. */
.field.waiting button.choose[role='combobox'] {
  border-color: var(--ds-border-warning, var(--fallback-warning));
}

.actions {
  display: flex;
  gap: var(--ds-space-100, 8px);
}

/* Save and Cancel held at the foot of the window while the form scrolls under
 * them. The form runs to more than two screens, and a reader who changed the
 * first choice had to scroll past every other one to find the way out - on a
 * phone, past two thousand pixels of it. Where the form is as tall as the frame
 * that holds it, as in the dialog Jira opens, this is where they were anyway. */
.form > .actions {
  position: sticky;
  bottom: 0;
  z-index: 1;
  padding: var(--ds-space-100, 8px) 0;
  background: var(--ds-surface, var(--fallback-surface));
  box-shadow: 0 -1px 0 var(--ds-border, var(--fallback-border));
}

.actions button {
  font: inherit;
  padding: var(--ds-space-075, 6px) var(--ds-space-150, 12px);
  border: none;
  border-radius: var(--ds-border-radius, 3px);
  color: var(--ds-text-inverse, #ffffff);
  background: var(--ds-background-brand-bold, #1868db);
  cursor: pointer;
}

.actions button[aria-disabled='true'] {
  /* `aria-disabled` rather than `:disabled`, because that is what the button
     carries: a disabled control is not in the tab order and says nothing when
     it is pressed, and the form wants both. Written as the pseudo-class for a
     while, which is a selector that matches nothing here - the refusing button
     and the working one computed to the same fill and the same cursor.

     An outline as well as the fill. The fill token is a low-alpha overlay
     meant to sit on a surface the host provides, and against the fallback
     palette on a dark background it left the button reading as plain text
     rather than as a control that is currently refusing. */
  background: var(--ds-background-disabled, transparent);
  color: var(--ds-text-disabled, var(--fallback-disabled));
  box-shadow: inset 0 0 0 1px var(--ds-border-disabled, #8590a244);
  cursor: default;
}

.actions button.subtle {
  /* The plain text colour rather than the subtle one. Measured beside the
     refusing Save: rgb(140, 155, 171) against rgb(133, 144, 162), two greys
     eight points apart, so nothing but the cursor told an available control
     from one that is refusing. Secondary is carried by having no fill, which
     is a channel the refusing button does not use. */
  color: var(--ds-text, var(--fallback-text));
  background: transparent;
}

/* The library saying that this grid's setup has moved on, and offering the
 * change.
 *
 * Marked rather than merely worded. It is the only thing in this form that
 * reports a fact from outside it - everything else is a choice the reader is
 * making - and a sentence that arrives on its own in a form of eleven controls
 * is one that reads as part of the paragraph above it.
 *
 * The button under the sentence rather than beside it. The sentence names up to
 * five choices and wraps to three lines in a dashboard's settings width, and a
 * button on the end of a wrapping paragraph lands somewhere different every
 * time the wording changes. */
.setup-moved {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
  margin-block: var(--ds-space-100, 8px);
  padding: var(--ds-space-100, 8px);
  border-inline-start: 2px solid var(--ds-border-accent-blue, #1868db);
  background: var(--ds-background-accent-blue-subtlest, rgba(24, 104, 219, 0.06));
}

.setup-moved .hint {
  margin: 0;
}

/* Following another Crosstab on the dashboard.
 *
 * The two buttons side by side where there is room and stacked where there is
 * not, like the library's pairs below. The name of the gadget chosen is
 * written out again under the list, because a select in a 346 pixel column
 * cuts the end off it, and the end is where two look-alikes differ.
 *
 * The line on a following gadget carries the same left rule the note about a
 * setup that moved on does: both say this gadget takes its settings from
 * somewhere else. */
.link-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-050, 4px);
  margin-block: var(--ds-space-050, 4px);
}

.chosen-crosstab {
  margin-block: var(--ds-space-050, 4px);
  overflow-wrap: anywhere;
}

.follows {
  margin: 0 0 var(--ds-space-050, 4px);
  padding-inline-start: var(--ds-space-075, 6px);
  border-inline-start: 2px solid var(--ds-border-accent-blue, #1868db);
}

/* The way out of following, and the way back in where the gadget followed
 * cannot be seen. Unstyled, each was the browser's own button stretched across
 * the column by the fieldset's grid: a flat grey bar that read as disabled in
 * the dark theme, beside a form whose every other button is drawn.
 *
 * Framed rather than filled. It is the one action in its block, but it changes
 * the form and stores nothing - Save at the foot of the form is the press that
 * stores, and two filled buttons in one short form leave the reader guessing
 * which of them is the one. As wide as its words, from the start of the line,
 * like the other buttons the form pairs with a sentence. */
.following .stop-following,
.following .choose-again {
  justify-self: start;
  font: inherit;
  padding: var(--ds-space-075, 6px) var(--ds-space-150, 12px);
  color: var(--ds-text, var(--fallback-text));
  background: none;
  border: 1px solid var(--ds-border-input, #8590a2);
  border-radius: var(--ds-border-radius, 3px);
  cursor: pointer;
}

.following .stop-following:hover,
.following .choose-again:hover {
  background: var(--ds-background-neutral-hovered, var(--fallback-neutral));
}

.following .stop-following:focus-visible,
.following .choose-again:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

/* The shared library of named setups, at the top of the settings form.
 *
 * Two rows of a control and a button, each pair on one line where there is room
 * and stacked where there is not. The button in each row is the one that acts
 * on what is beside it, which is why they are paired rather than gathered into
 * a row of their own at the bottom - a form of eleven controls with three
 * buttons under it is a form where nobody knows which button belongs to what. */
.setup-pick,
.setup-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-050, 4px);
}

/* The control takes the width and the button takes what it needs. A select of
   names and a name being typed are both as long as somebody made them, and a
   button that moves as the text beside it grows is a button pressed by
   accident. */
.setup-pick .choose,
.setup-name input {
  flex: 1 1 12ch;
  min-width: 0;
  font: inherit;
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-raised, var(--fallback-surface));
  border: 1px solid var(--ds-border-input, #8590a2);
  border-radius: var(--ds-border-radius, 3px);
  padding: var(--ds-space-075, 6px) var(--ds-space-100, 8px);
}

/* The same buttons the actions row has, including the refusing state: this is
   the one place in the app where a button is offered before the thing it acts
   on exists, so it spends most of its life saying no. Use these and Copy them,
   under another Crosstab on this dashboard, are the same pair in the same form
   and refuse the same way until one is chosen. */
.setups button,
.link-actions button {
  font: inherit;
  padding: var(--ds-space-075, 6px) var(--ds-space-150, 12px);
  border: none;
  border-radius: var(--ds-border-radius, 3px);
  color: var(--ds-text-inverse, #ffffff);
  background: var(--ds-background-brand-bold, #1868db);
  cursor: pointer;
}

.setups button.subtle,
.link-actions button.subtle {
  color: var(--ds-text, var(--fallback-text));
  background: transparent;
}

.setups button[aria-disabled='true'],
.link-actions button[aria-disabled='true'] {
  background: var(--ds-background-disabled, transparent);
  color: var(--ds-text-disabled, var(--fallback-disabled));
  box-shadow: inset 0 0 0 1px var(--ds-border-disabled, #8590a244);
  cursor: default;
}

.setups button:focus-visible,
.link-actions button:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

/* The fallback fields, under the one they fall back from.
 *
 * A fieldset because they are one question with several answers, and its legend
 * is what says so. Borderless: the browser's default frame around a fieldset is
 * a box inside a form that is already a narrow column, and it reads as a second
 * form rather than as part of this one. */
fieldset.also {
  border: 0;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--ds-space-050, 4px);
  /* As wide as the form and no wider. A fieldset is as wide as its widest
     content by default, and a select is as wide as its longest option - here
     the name of another gadget - so in a narrow column the fieldset and its one
     grid column grew past the gadget's edge, and every sentence inside was
     laid out at that width and clipped. Both are needed: the fieldset alone
     narrowed, the column still took the select's width. */
  min-inline-size: 0;
  grid-template-columns: minmax(0, 1fr);
}

fieldset.also legend {
  padding: 0;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* Where the issues come from, which is a question with two answers and not a
 * list of them. The row of plates inside it has its own bottom margin from
 * `.modes`, and the fieldset is the column gap's business - so this takes the
 * margin off rather than adding a second one. */
fieldset.where-from .modes {
  margin-bottom: 0;
}

/* The same plates joined into one segmented control, Atlassian's shape for a
 * choice of one: one outline around both and the chosen one filled inside it.
 * Apart, with a gap between them and no edge around the pair, they read as two
 * tabs that apply together. As wide as its plates rather than the column, so
 * the outline closes where the answers end; and the gap closed, so nothing
 * between two plates reads as a third place to click. */
.modes.segmented {
  justify-self: start;
  width: fit-content;
  max-width: 100%;
  gap: 0;
  padding: var(--ds-space-025, 2px);
  border: 1px solid var(--ds-border, var(--fallback-border));
  border-radius: var(--ds-border-radius-100, 4px);
}

/* What the dot on a plate stands for, said under the control in the plain
 * colour of a sentence rather than the muted one of a hint: it is the line
 * that says which source Save stores, and read past it is the question the
 * reader came back with. */
.source-held-said {
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
}

/* A line of JQL, which wraps and is read back as often as it is written.
 *
 * Monospaced, because the thing being checked in it is a bracket or a quote in
 * the wrong place and a proportional face hides exactly those. The same box as
 * every other control here otherwise: this is one field of the form and not a
 * console.
 *
 * Resizable up and down only. A textarea a reader drags wider than the gadget
 * makes the dashboard column scroll sideways, which moves every other gadget
 * on the page. */
.field textarea.jql {
  font: inherit;
  font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, monospace);
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-raised, var(--fallback-surface));
  border: 1px solid var(--ds-border-input, #8590a2);
  border-radius: var(--ds-border-radius, 3px);
  padding: var(--ds-space-075, 6px) var(--ds-space-100, 8px);
  resize: vertical;
  min-width: 0;
}

/* Each caveat on its own line. They are read before the number is quoted, and
   a sentence wrapped into the paragraph above gets skipped.

   A row rather than a block, so the mark in front of each one sits beside the
   whole sentence and the second line of a sentence that wraps starts under the
   first word rather than under the mark. On a dashboard column most of these
   wrap, so that indent is the difference between a list and a paragraph with
   pictures in it. */
.caveat {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
  margin-top: var(--ds-space-050, 4px);
  /* A shade back from the sentence above, which is the figure the reader came
     for; these qualify it. Solid prose in one colour and one weight is what the
     owner saw and called a wall, and the two are not the same kind of thing:
     one says what the grid holds and the rest say what to know before quoting
     it. The step is a token rather than an alpha, so it holds in both themes. */
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* The sentence a caveat qualifies, which is the one carrying the figures. Plain
   text colour, so the pair reads as a statement and its footnotes rather than
   as one paragraph that will not end. */
.hint.numbers {
  color: var(--ds-text, var(--fallback-text));
}

/* The one figure the second gadget draws, with nothing to compare it against.

   Sized in `em` rather than pixels, so it follows whatever the host sets on
   the dashboard: Jira's own compact mode and a reader's browser zoom both
   change the root size, and a figure pinned at 32px is the one thing on the
   card that would not move with them.

   `tabular-nums` for the same reason the table cells have it - a figure that
   is redrawn every refresh must not shift the sentence under it as the digits
   change. `line-height` is tightened because the default leading around a
   2.4em figure opens a gap wider than the caption above it. */
.figure-alone {
  margin: var(--ds-space-100, 8px) 0 0;
  font-size: 2.4em;
  font-weight: 600;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--ds-text, var(--fallback-text));
}

/* A caveat the reader can act on stays warning-coloured, whichever storey it is
   in: it is the reason the table looks the way it does. */
.hint .caveat.warn {
  color: var(--ds-text-warning, var(--fallback-warning));
}

/* The sentence the caveats qualify, marked like them and coloured like itself.
   It is the first line of the same list, so it takes the same row; the step in
   colour between it and them is what `.hint.numbers` already says.

   Its own class rather than a `.caveat` variant, and not only for the colour.
   A caveat is a claim about the grid, and the suite proves it by reading every
   number out of every `.caveat` on screen and asking the grid whether it holds
   it. This is the sentence that says when the grid was read: wearing that
   class, the day of the month went in as a claim and the test caught it. */
.sentence {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
  color: inherit;
}

/* The mark in front of a sentence.

   Sized in pixels rather than `em` because it is a drawing and not type: at
   the size Jira's compact mode sets, a mark scaled with the text loses the
   two-pixel features these outlines are made of and arrives as a grey smudge.

   Nudged down by a fraction of a line rather than aligned on the baseline: an
   `svg` sits on the baseline by its bottom edge, so a twelve-pixel square
   baselined against fourteen-pixel type hangs a third of itself below the
   first line of the sentence beside it.

   `flex: none` because a mark that shrank to fit a long sentence would be a
   different mark on every line.

   Its own class rather than `.glyph`, which is taken by the issue-type icon in
   a table heading: unscoped, these four properties shrank every one of those
   to twelve pixels and dropped it a fifth of a line. */
.sign {
  flex: none;
  width: 12px;
  height: 12px;
  margin-top: 0.22em;
}

/* The sentences outside the two storeys that carry a mark of their own: what
   is off the right of the window, and what a press would draw instead. Laid
   out like a caveat because that is what they are - the only difference is
   that these sit beside the table rather than under it. */
.hint.away,
.hint.turn,
.drawn .hint {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
}

/* A gadget is a small box on a crowded page. The table scrolls inside this,
   and the dashboard never scrolls sideways because of it — that would move
   every other gadget on the page for one person reading one table. */
.grid-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  /* The window worth landing in, declared to the browser rather than only
     computed in JavaScript. Two columns of this table never scroll and both lie
     on top of the cells that slide beneath, so the scrollport and the part of
     it a reader can see are different rectangles: without this, `focus` and
     `scrollIntoView` both consider a covered cell to be in view already.

     The two widths are measured and written onto this element, because the row
     labels are capped in characters and a character is not a fixed width. */
  scroll-padding-inline: var(--frozen-start, 0px) var(--frozen-end, 0px);
  /* And rest on a column boundary rather than anywhere.
   *
   * A figure is aligned right, which is exactly where the frozen Total column
   * begins, so a cell coming to rest half under it shows its heat fill and none
   * of its digits - a brightly coloured cell with nothing written in it, in a
   * product whose whole argument is the difference between empty, nought and
   * unmeasured. Measured on the preview page at the width this gadget lives at:
   * 348 of 468 scroll positions had at least one such cell, the worst showing
   * 78 pixels of fill with the number entirely covered.
   *
   * The snap areas are the column headings, aligned to the end of the padded
   * scrollport - which is the seam, not the window edge - so every rest puts a
   * column boundary exactly where the Total begins. `proximity` rather than
   * `mandatory`: the far ends of the scroll are not column boundaries and
   * `mandatory` would refuse to rest at either of them.
   *
   * Measured again with this on: the browser now allows seven rest positions,
   * six of them with no such cell at all, and the seventh - the far left -
   * showing three pixels of one. */
  scroll-snap-type: x proximity;
  /* Room under the last row for the focus ring of its figures, the corner
     total among them: scrolling one way clips the other as well, and the ring
     is drawn outside the figure. As tall as the ring stands out - the offset
     and the width set on `.figure:focus-visible`. */
  padding-bottom: 4px;
  /* The box the hidden text of a cell is placed against. Without it that text
     is placed against the frame, outside this box, so it is not clipped and
     widens the frame and the gadget with it. */
  position: relative;
}

.grid thead th:not(.total):not(.corner) {
  scroll-snap-align: end;
}

/* An edge with something behind it.
 *
 * On macOS overlay scrollbars are the default, so a grid wider than its box
 * looks finished: measured on the preview page, a nine-column grid hides 314
 * pixels of itself and the only hint is a column name clipped at the right
 * edge. A clipped word is an artefact; this is an affordance.
 *
 * On the frame rather than on the scrolling box, because anything positioned
 * inside a scroller travels with its content — it would slide away from the
 * very edge it marks. A gradient painted under the table would not do either:
 * the frozen label and every shaded cell carry their own backgrounds.
 *
 * Drawn from the text colour, so it works in either theme without a second
 * palette, and faint: it marks a boundary rather than competing with the
 * numbers beside it. */
.grid-frame {
  position: relative;
}

/* Only as tall as the table it belongs to. `top: 0; bottom: 0` on the frame
   drew it down the side of the advice paragraph as well - measured, 68 of its
   274 pixels, a quarter of the mark, beside a sentence that does not scroll and
   has nothing behind it. */
.grid-frame::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 14px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .grid-frame::before,
  .grid .edge {
    transition: none;
  }
}

.grid-frame::before {
  left: 0;
  background: linear-gradient(
    to right,
    color-mix(in srgb, currentColor 16%, transparent),
    transparent
  );
}

.grid-frame.has-start::before {
  opacity: 1;
}

.grid {
  border-collapse: collapse;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.grid th,
.grid td {
  /* On the cell rather than on the figure inside it. A cell holding nothing
     draws no button and had no height of its own, so a view with blanks in it
     came out shorter than the same table with figures in every cell - the rows
     of a share table where a row holds both signs went from 41 pixels to 33,
     and the whole card moved under the reader. */
  height: 28px;
  /* Two pixels above and below rather than six, which is the whole of what a
     row can give back.
     A row was 42.5 pixels and 29 of them are the figure itself: every figure
     is a button that opens the issue search behind it, and that button carries
     `min-height: 24px` of its own because 24 is the floor WCAG 2.5.8 puts
     under a target. One pixel is the rule under the row. The remaining twelve
     were this padding, and they bought nothing that was ever measured.
     Measured on the nine-status table in `preview.html`: 42.5px a row and
     226px of table at six, 34.5px and 186px at two - 17.7% shorter for the
     same figures, with the target untouched at 29px in both. A row whose label
     wrapped to two lines stays tall, 49px rather than 57, which is right:
     those rows are tall because of their text and not because of this.
     The competitor on the same dashboard draws 25px rows, and cannot be caught
     without giving up the thing the height is for - their figures are text
     rather than links, so they have no target to hold open.
     The floor above is unchanged by this and still does its job: an empty cell
     comes out two pixels under a filled one here, exactly as it did at six. */
  padding: var(--ds-space-025, 2px) var(--ds-space-100, 8px);
  border-bottom: 1px solid var(--ds-border, var(--fallback-border));
  text-align: right;
}

.grid thead th {
  border-bottom-width: 2px;
  vertical-align: bottom;
}

/* The heading is the control, so the button has to be the heading.
 *
 * A second glyph beside every column name would cost the one thing this table
 * has none of: the whole product is a grid in a dashboard column, and a wide
 * grid is the complaint it exists to answer. So the button carries the
 * heading's own type and alignment and draws nothing of its own at rest - it
 * is the pointer, the focus ring and the arrow that say it can be pressed. */
.grid th .sort {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  border: 0;
  padding: 0;
  margin: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: inherit;
  cursor: pointer;
}

/* What None holds, as a tooltip over the whole face of the button. It takes no
 * box of its own, so the heading and its arrow lay out as they do on every
 * other column. */
.grid th .sort .hinted {
  display: contents;
}

/* The arrow, in the space it takes whether or not it is drawn.
 *
 * Reserved rather than added on the press: a column that grows by the width of
 * an arrow the moment it is sorted moves every column to the right of it, and
 * on a grid already too wide for its box that is the table shifting under the
 * hand that sorted it. Six pixels, held on every heading of every grid, so a
 * press changes what is drawn in that space and never how much of it there
 * is. */
.grid th .sorted {
  width: 6px;
  flex: none;
}

.grid th .sorted.down::before {
  content: '▾';
}

.grid th .sorted.up::before {
  content: '▴';
}

/* Where the arrow is not the answer: a heading nobody has sorted by says so
 * under the pointer and under focus, rather than carrying an arrow at rest on
 * every column of every grid. */
.grid th .sort:hover .sorted::before,
.grid th .sort:focus-visible .sorted::before {
  content: '▾';
  opacity: 0.4;
}

.grid th .sort:hover .sorted.down::before,
.grid th .sort:focus-visible .sorted.down::before,
.grid th .sort:hover .sorted.up::before,
.grid th .sort:focus-visible .sorted.up::before {
  opacity: 1;
}

.grid th .sort:hover .sorted.up::before,
.grid th .sort:focus-visible .sorted.up::before {
  content: '▴';
}

/* A label the cap cannot hold, drawn over two lines rather than cut off after
 * one.
 *
 * Measured in a browser at 340 pixels with status down the side: the cap
 * clips a couple of characters short once the cell's padding is inside it, so
 * on one line `Ready for Development`, `Ready for Grooming` and `Ready for QA`
 * all drew as `Ready fo…` - three adjacent rows carrying 56, 2 and 11 story
 * points that the reader could not tell apart. The only thing separating them was the tooltip,
 * and a tooltip does not exist on a touch viewport.
 *
 * Two lines rather than a wider cap, because the cap is what buys the window
 * back: widening it to fit `Ready for Development` on one line would return the
 * table to two readable columns of eleven. The reader pays in height, which
 * they can scroll, instead of in width, which they cannot.
 *
 * Two rather than three: the ellipsis then falls on the second line, so a label
 * longer than both still says it was cut - and the rows stay a size somebody
 * can scan down. `anywhere` rather than `break-word` so a single long word - a
 * component with no spaces in it - breaks instead of setting the column's width
 * on its own and undoing the cap. */
.grid th .label {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  white-space: normal;
  overflow-wrap: anywhere;
  /* The cap, on the label rather than on the cell, and with a preferred width
     of `max-content` under it.
     The cap is the same for both axes now: the columns had none at all, and a
     55-character status drew a column 404 pixels wide inside a 308 pixel
     window - one column, no figures, and no way to reach the rest of the table
     but through it. A column is as wide as the widest thing in it, and the
     widest thing was never the number.
     `max-content` is what stops the table's auto layout squeezing a heading
     that now wraps: measured in a browser without it, all nine columns of an
     eleven-status board came out 32 pixels wide - two characters a line, every
     one of them clipped, which is worse than the 404 it was fixing. With it, a
     heading asks for its whole text and is granted the cap; a heading shorter
     than the cap keeps its own width, so a board of short statuses does not pay
     for a board of long ones. */
  width: max-content;
  /* Twelve characters, at every width and on both axes. It used to widen to
     eighteen where there was room, and that step is gone: behind the container
     query it made the gadget show less at 392 pixels than at 391 - measured,
     the table went from 924 to 1107 while the window grew by one, and the
     fully readable columns fell from three to one. A fixed cap is the point;
     which number it is is the measurement below.

     It was ten, and ten cut one word that a reader meets constantly. At ten
     `Ready for Development` draws as `Ready for` / `Develop…` - and since the
     lozenge arrived that ellipsis sits inside a coloured box, which reads as a
     pill that broke rather than as a name that is long. It was the only cut
     label on the whole listing frame. At twelve the label wraps at its own
     space, both words whole, and nothing on that frame is cut at all.

     What the two characters cost, measured in a browser at the 340px gadget
     this cap exists for - a 308px window with a 48px frozen Total beside the
     frozen label:

     | Row axis                  | at 10ch      | at 12ch      |
     | ------------------------- | ------------ | ------------ |
     | Issue types (short names) | 72px, 188 left | 72px, 188 left |
     | Statuses (one long name)  | 98px, 162 left | 112px, 148 left |

     So the cap charges nothing at all until a label would have been cut, and
     then charges fourteen pixels - 8.6% of what is left to read the table
     with. A column heading pays in scroll distance rather than in window, and
     is the same twelve for a plainer reason: two labels of the same length
     cut at different places would be a rule a reader cannot learn.

     The figures this paragraph used to carry - 156px frozen, 103px left - were
     taken before the label was ever drawn as a lozenge, and a lozenge sets its
     text a size down, so `ch` on it is a smaller unit. They had been describing
     a cell 58 pixels wider than the one beside them ever since.

     Either way it is the narrow half of the tradeoff rather than both: the full
     label is in the DOM, a screen reader hears all of it, and the tooltip
     carries it for everybody else. */
  max-width: 12ch;
}

/* The id that tells two values of the same name apart.
 *
 * Beside the name rather than in it, and a size down. It used to be part of
 * the label - `Bug (#10012)` - which on a site with five issue types called
 * Bug wrapped at the ten-character cap and made every row of the table two
 * lines tall: measured beside a competitor's gadget on the same dashboard and
 * the same board, twice the height for the same data.
 *
 * Still in the label element, so the cap and the clamp above apply to both
 * together and a long name is still cut at the same place. The full text is
 * in the tooltip and in what a screen reader hears, as it always was. */
.heading .qualifier {
  font-size: max(12px, 0.8em);
  font-weight: 400;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
  font-variant-numeric: tabular-nums;
}

/* The two parts of a heading on one line: the icon and the capped label.
 *
 * A wrapper rather than flex on the cell itself. `display: flex` on a `th`
 * stops it being a table cell at all - the browser wraps it in an anonymous
 * one - and the row labels are sticky, which is a property of the cell. The
 * frozen column would have come unstuck to line up an icon.
 *
 * `inline-flex` rather than `flex` so the cell's own alignment still decides
 * where the heading sits: right for a column, left for a frozen row label.
 *
 * `baseline` puts the icon on the text's baseline rather than the box's top,
 * which is where it has to be for a label that wrapped to two lines - aligned
 * to the top of the box it floats above the first line by the clamp's leading.
 *
 * Scoped to the heading and not to the grid's cells, here and in the rules for
 * the icon and the lozenge below: the same component names a cell in the list
 * Crosstab Change draws and heads the table under Crosstab Chart, and those
 * sit beside the grid on one dashboard. Only the cap and the clamp stay on
 * `.grid th`, because only a frozen column has to be kept that narrow. */
.heading {
  display: inline-flex;
  align-items: baseline;
  gap: var(--ds-space-050, 4px);
  text-align: left;
}

/* The issue type's own icon, in the accent Jira draws it in.
 *
 * `flex-shrink: 0` because the label beside it is allowed to wrap and the icon
 * is not: without it a narrow column squeezed the square into a rectangle and
 * the bug came out as an insect drawn on a stretched page.
 *
 * The fallbacks are the exact colours the avatar endpoint serves, so the icon
 * is right with no tokens at all; inside Jira the token wins, and that is a
 * gain rather than a compromise. The endpoint answers with one fixed colour
 * for both themes, and four of its five are the dark theme's values - the
 * image this replaces drew a light dashboard's bug in a dark theme's red. */
.heading .glyph {
  flex-shrink: 0;
  align-self: center;
}

/* The margin row has no icon and still lines up with the rows that do: the
   icon's 16 pixels and the gap beside it, as padding rather than as a spacer
   element, so nothing is added to what a screen reader reads. */
.grid th.past-a-glyph {
  padding-inline-start: calc(var(--ds-space-100, 8px) + 16px + var(--ds-space-050, 4px));
}

.heading .glyph.blue {
  color: var(--ds-icon-accent-blue, #4688ec);
}
.heading .glyph.lime {
  color: var(--ds-icon-accent-lime, #6a9a23);
}
.heading .glyph.orange {
  color: var(--ds-icon-accent-orange, #e06c00);
}
.heading .glyph.purple {
  color: var(--ds-icon-accent-purple, #bf63f3);
}
.heading .glyph.red {
  color: var(--ds-icon-accent-red, #f15b50);
}

/* A status heading, in the colour Jira gives its category.
 *
 * Drawn at every width, which is a decision rather than the default. The
 * padding a lozenge needs is width, and this file has measured what width
 * costs elsewhere - but not here: the frozen cell whose cap that argument is
 * about is the row label, and a column heading scrolls with its column. So the
 * price of the colour is scroll distance rather than a figure a reader cannot
 * reach, and it is one the competitor on the same dashboard pays at the same
 * width. Measured on the listing frame: eleven statuses draw 1102 across where
 * they drew 1024, seven pixels a column.
 *
 * `display` is not touched: the clamp above is what keeps a long status to two
 * lines, and a lozenge that unclamped itself would draw the 55-character
 * status this file went to some trouble to cap. */
.heading .label.lozenge {
  padding: 2px 6px;
  border-radius: 3px;
  font-size: max(12px, 0.9em);
  font-weight: 600;
  /* Not upper case, though that is how Jira's older lozenge was set, and how
     the competitor on the same dashboard still sets it. Upper case is about
     fifteen per cent wider at the same size, and there is a cap: `Canceled`
     fits on one line as it is written and broke across two as `CANCELE` / `D`,
     which is a coloured box with a word torn in half in it. The colour is what carries the category; the casing was
     decoration that cost the word. */
  background: var(--ds-background-neutral, var(--fallback-category-neutral));
  color: var(--ds-text, var(--fallback-category-neutral-text));
}

.heading .label.lozenge.indeterminate {
  background: var(--ds-background-information, var(--fallback-category-progress));
  color: var(--ds-text-information, var(--fallback-category-progress-text));
}

.heading .label.lozenge.done {
  background: var(--ds-background-success, var(--fallback-category-done));
  color: var(--ds-text-success, var(--fallback-category-done-text));
}

/* Row labels stay put while the columns scroll. A column three screens to the
   right with nothing to attach it to is worse than a grid that does not fit.
 *
 * Capped, because a sticky column wider than the window makes the scrolling
 * decorative. Measured on the rendered page: a forty-character label — an epic
 * summary, a component, a sprint — took 335 of the 346 pixels available, 96.8%
 * of the window, and left eleven pixels of the table it was labelling.
 *
 * Eighteen characters rather than a percentage: a percentage inside a table is
 * a share of the table, which is the thing that grows when the label does. At
 * the gadget's text size that is 140px of text, and 156px with its padding —
 * 45% of the 346px a dashboard column gives. The padding is outside the width,
 * which is what makes the cell wider than the cap says.
 *
 * The full label stays in the DOM, so a screen reader hears all of it, and the
 * element carries it as a tooltip for everybody else. */
.grid th[scope='row'],
.grid .corner {
  position: sticky;
  left: 0;
  text-align: left;
  font-weight: 600;
  background: var(--ds-surface, var(--fallback-surface));
  /* Above whatever slides under it. A sticky cell with no z-index sits at
     `auto`, and anything positioned later in the document paints over it: the
     mark on an understated figure carries `position: relative`, so those
     figures were drawn on top of the frozen label and the two read as one
     word. An opaque background is not enough on its own. */
  z-index: 1;
  /* The seam. Cells slide under a frozen column with nothing to mark where one
     ends, so a reader cannot tell it from the edge of the table - and on the
     corner of the header, which had no seam at all, the tail of a column
     sliding under it read as the heading: `opment` at one scroll position and
     `-Work` at another. */
  box-shadow: 1px 0 0 var(--ds-border, var(--fallback-border));
}

.grid .corner {
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
  font-weight: 400;
}

/* The column of row totals stays put while the columns scroll, for the same
   reason the row labels do: it is the anchor every row resolves to. Measured on
   the rendered page before this existed - in a dashboard column 75.2% of the
   table was off screen, the two fully visible columns carried 8 of 6893 points,
   and the Total column was outside the window at every width tried, including
   the full width of a dashboard.
 *
 * The seam is on its left, mirroring the row labels: cells slide under it, and
 * without a line there is nothing to say where the scrolling part ends. */
.grid .edge {
  position: sticky;
  right: 0;
  background: var(--ds-surface, var(--fallback-surface));
  z-index: 1;
  box-shadow: -1px 0 0 var(--ds-border, var(--fallback-border));
  transition: box-shadow 120ms ease-out;
}

/* The affordance for "there is more of this table to the left of the totals"
   is cast by the frozen column itself rather than painted on the frame.
 *
 * The frame was the right place while nothing was frozen on that edge - the
 * comment above still holds for the left one - but a gradient at `right: 0`
 * now lies across the totals it was meant to point past. A sticky cell is the
 * one thing inside a scroller that does not travel with the content, so it can
 * carry the mark itself, and it sits exactly where the scrolling part ends. */
.grid-frame.has-end .grid .edge {
  box-shadow:
    -1px 0 0 var(--ds-border, var(--fallback-border)),
    -8px 0 8px -8px color-mix(in srgb, currentColor 40%, transparent);
}

/* A figure the frozen columns lie across.
 *
 * Both of them are opaque, and they have to be: a number sliding under a
 * translucent one draws two numbers on top of each other. So a column resting
 * half under one shows the digits that clear the seam and nothing to say the
 * rest are behind it - 122.5 drawn as `122.`, and a full stop after a number
 * is a thousands separator to most of the people reading this.
 *
 * The colour goes rather than the element: the cell keeps its shade, so it
 * still reads as a cell with something in it, and the figure stays in the
 * document for anybody listening to the table rather than looking at it. A
 * reader who arrows onto it is carried into the clear window first, so the
 * figure they land on is drawn.
 *
 * Measured at the width a dashboard gives this gadget: at the far left of the
 * scroll - the position it opens in - one column rests under the totals, and
 * that is the one rest position the scroll snapping cannot take away, because
 * the end of a scroll is not a column boundary.
 *
 * Everything inside the cell as well as the cell, because `color` reaches only
 * what inherits it and three things in a figure do not: the line under one that
 * can be pressed, the count of issues beside it, and a nought. Each stayed
 * drawn - a cell meant to be blank read `__ 14`, where 14 is how many issues
 * are behind the figure and not the figure. A number that is not the one the
 * cell holds is worse than the clipped digits any of this is here to prevent. */
.grid td.unread,
.grid td.unread * {
  color: transparent;
  text-decoration-color: transparent;
}

/* A column partly under the totals, whose figures fit in the part that is not.
 *
 * Drawn short rather than blank: the figure stays set to the right, of a
 * narrower box that ends at the seam, so it reads as the column's figure and
 * the mark for what is missing stays in its corner. Blanked, a column with
 * most of its width in the clear read as a column of broken cells. How far
 * short is measured per column, in `--short`, and only at the start of the
 * scroll: every other rest is taken to a column boundary, so there is no
 * column across the seam to shorten. */
.grid td.short .drill,
.grid td.short .figure {
  width: calc(100% - var(--short, 0px));
}

/* A heading over a column drawn short or blank, drawn the same way.
 *
 * The opaque totals lay across it as across the figures, so a heading set to
 * the right of a column resting under them was cut mid-word. Short, it ends at
 * the seam - moved rather than padded, and narrowed by a share of its cell
 * rather than by a width of its own, so the column is as wide as it was and
 * the distance measured on it still holds; the label ends in its ellipsis
 * inside what is left. Over a blank column it is not drawn either,
 * nor pressed by a pointer that cannot see it, and not removed: a reader
 * listening to the table still hears it. */
.grid thead th.short .sort {
  position: relative;
  inset-inline-end: var(--short, 0px);
  max-width: calc(100% - var(--short, 0px));
}

/* And the label inside it held to the button, which the narrower button alone
 * does not do: the label is as wide as its whole text, so it ran on past the
 * end of the button and under the totals - `Admin console` read `Admin c`
 * against them over figures drawn short and whole. A share of the button, so
 * the width the column takes from its heading is what it was and the distance
 * measured on it still holds.
 *
 * On one line, ending in its ellipsis at the seam. Allowed its usual two, the
 * few pixels left broke the word itself across them - `Admi` over `n…` - which
 * reads as a broken heading rather than a short one. */
.grid thead th.short .sort .heading {
  min-width: 0;
  max-width: 100%;
}

/* No share of its own on the label: that would replace the twelve-character
 * cap, and a long label would then ask for its whole text on one line and
 * widen the column it is a heading of. */
.grid thead th.short .sort .label {
  min-width: 0;
  display: block;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.grid thead th.unread .sort {
  opacity: 0;
  pointer-events: none;
}

.grid .total {
  font-weight: 600;
}

.grid tfoot .total {
  border-bottom: none;
  border-top: 2px solid var(--ds-border, var(--fallback-border));
}

.grid .corner-total {
  border-top: 2px solid var(--ds-border, var(--fallback-border));
}

/* A row, so the mark beside a banner sits against the whole sentence and the
   second line of one that wraps starts under the first word. Every banner on
   this card wraps at the width a gadget gets. */
.banner {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
  margin: 0 0 var(--ds-space-100, 8px);
}

/* A kept grid is not a warning - nothing is wrong with it - but it must not
   read as today's numbers either. A rule down the leading edge, in the same
   blue the rising side of the scale uses, says "this is set apart" without
   claiming anything is broken. */
.kept-banner {
  border-left: 3px solid var(--ds-border-accent-blue, #1868db);
  padding-left: var(--ds-space-100, 8px);
}

/* And somebody else's grid, which is a different claim and had the same rule.

   Blue down the edge says an earlier moment, which is true of a kept grid and
   is not what is different about this one: these figures were read with
   another person's permissions, and no amount of looking at them tells the
   reader that. Two states that differ in the one way this feature must never
   blur, drawn identically, is the defect the wording exists to prevent, one
   layer down.

   Purple rather than a warning colour. Nothing is wrong with a published grid
   - it is the whole point of the feature - and the card already spends its
   warning colour on the read that stopped short. */
.published-banner {
  border-left: 3px solid var(--ds-border-accent-purple, #8270db);
  padding-left: var(--ds-space-100, 8px);
}

/* A banner whose sentence carries a control keeps the two in one box, so the
   row is the mark and the line rather than the mark, the words and the
   button. Three items wrap as three, and the press landed under the mark. */
.offered {
  min-width: 0;
}

/* The placeholder is the shape of the answer rather than a spinner: a gadget
   that flashes one and then reflows the dashboard around a table is one the
   reader has to find twice. */
.skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-100, 8px);
  margin-top: var(--ds-space-100, 8px);
}

.skeleton-row {
  height: 12px;
  border-radius: var(--ds-border-radius, 3px);
  background: var(--ds-background-neutral, var(--fallback-neutral));
}

.skeleton-row:nth-child(2) {
  width: 80%;
}

.skeleton-row:nth-child(3) {
  width: 55%;
}

/* Which question the grid is answering. Three visible options rather than a
   closed list: the point of the control is that the answers differ, and a
   select hides that there is a choice at all. */
/* The row of questions above the table: which figure each cell shows, and
   whether to point out the ones that had to leave issues out. Wrapping,
   because the second question carries the field's own name and a site can call
   its field anything. */
/* The caption and the mark in one row, which wraps on a narrow column.
 *
 * Measured at 346 pixels with a real caption: 40 pixels without the mark and
 * 68 with it, because the caption takes the whole width and the mark falls
 * below it. That is four pixels against the row of modes it came out of, not
 * the 32 first claimed. It sits here because it belongs to the figures the
 * caption names, which is the better reason of the two. */
.says {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ds-space-100, 8px);
}

.says .grid-caption {
  margin-bottom: 0;
}

/* How old the figures are, and the press that reads them again: quiet, because
   it is about the figures rather than one of them.

   A line of its own, straight under the caption. Sharing the row, it sat at
   the right where there was no mark and fell to the left of the next line
   where there was one, so it moved as the reader changed view. The captions
   of every view are the same height, so under them is one place in all four. */
.read-ago {
  flex-basis: 100%;
  display: flex;
  align-items: baseline;
  gap: var(--ds-space-075, 6px);
  margin: 0;
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
}

.read-again {
  font: inherit;
  min-height: 24px;
  padding: 0 var(--ds-space-050, 4px);
  border: none;
  background: none;
  color: var(--ds-link, var(--fallback-selected-text));
  text-decoration: underline;
  cursor: pointer;
}

.read-again[aria-disabled='true'] {
  color: inherit;
  cursor: default;
}

.read-again:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 2px;
}

/* What is behind a disclosure under the table, and how to open one.
 *
 * Measured on the same column: the card was 644 pixels tall and the table
 * window 198 of them, with this note taking 176 - nine lines of grey text
 * whose most important sentence was the ninth. The numbers stay in front of
 * the reader; the explanations sit here. Nothing is removed: what is behind a
 * disclosure is still in the accessible tree, still copied with the page, and
 * still written into the exported file, where it is not behind anything. */
.why summary,
.plot-figures summary {
  cursor: pointer;
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
  padding: var(--ds-space-050, 4px) 0;
  user-select: none;
}

/* An explanation whose first sentence is its summary. It reads as the hint it
 * is rather than as a question, so it takes the hint's own space and none of
 * the padding a disclosure under the table has. */
.why.more summary {
  padding: 0;
}

.why.more > .hint {
  margin-top: var(--ds-space-050, 4px);
}

.why summary:focus-visible,
.plot-figures summary:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 2px;
}

/* Every view, wrapping where the card is too narrow to seat them in a row.
 *
 * Without the wrap the row lays them all out at their natural widths whatever
 * the box is, and a flex line that cannot shrink does not clip - it overhangs.
 * At the width this gadget is installed at that put the last view 65px past
 * the right edge of the card, cut mid-word, and took the card's own scrollWidth
 * past its clientWidth with it: everything else on the card measured its own
 * overflow from that, which is how a status heading with room for its whole
 * label still came out cut. `docs/measurements.md` carries both figures.
 *
 * Wrapping rather than scrolling, like `.exports`, `.asks` and `.chart-key` above
 * and below it. A row of radios a reader has to scroll sideways hides a view
 * behind a gesture nothing on the card suggests.
 *
 * It costs no height either, which is the opposite of what a wrap usually
 * trades: the row is 11px shorter wrapped than not. Held in one line the
 * labels are squeezed until each of them breaks inside itself, so `% of row`
 * stands three words tall; allowed to wrap between labels they break where
 * the spaces already are. */
.modes {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-bottom: var(--ds-space-100, 8px);
}

/* The table or the chart. Its own row under the views rather than beside them:
   the views are four answers to one question and this is a different question,
   and a fifth control in that row reads as a fifth view. */
.draws {
  display: flex;
  margin-bottom: var(--ds-space-100, 8px);
}

/* The chart stands in the table's place, at a height of its own.
 *
 * Fixed rather than a ratio, which `ui/Chart.jsx` gives the reason for: a
 * gadget does not choose the width it is given, and a ratio makes the chart
 * shortest exactly where the column is narrowest. The canvas fills the box,
 * and the library is told not to keep an aspect ratio so that it follows it. */
.chart-box {
  position: relative;
  width: 100%;
}

.chart {
  display: block;
  width: 100%;
  height: 100%;
}

/* The names of the series, under the chart rather than on it.
 *
 * The library draws its own legend inside the same box as the bars, and ten
 * statuses across the top is five lines of it - so on a dashboard column the
 * names took more of the picture than the picture. Here it wraps where it
 * likes, it is text a browser can find and copy, and the bars keep the height
 * the box was given. */
.chart-key {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-050, 4px) var(--ds-space-150, 12px);
  margin: var(--ds-space-075, 6px) 0 0;
  padding: 0;
  list-style: none;
  font-size: max(12px, 0.85em);
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
}

.chart-key li {
  display: flex;
  align-items: center;
  gap: var(--ds-space-050, 4px);
}

/* The one entry that is not a column, on a line of its own at every width.
 *
 * Measured on the built page, seven entries: at a legend 270px wide they wrap
 * 2 / 3 / 2 and the fold shares its line with a status, and at 312px they wrap
 * 3 / 3 / 1 and it does not. So whether the last entry looked like one of the
 * list came down to where the wrap happened to fall, and the narrower gadget -
 * the one this is drawn in most - was the one it fell wrong in.
 *
 * Worth a rule because the fold is drawn in a neutral for exactly this reason,
 * and a neutral in a row of colours is still one of the row. Position is the
 * channel that says it is not, and leaving that to the wrap leaves it to the
 * width of somebody else's dashboard column.
 *
 * `flex-basis` rather than a break before it: the six named entries keep
 * wrapping as they like, and only this one is guaranteed its own line - which
 * puts it against the sentence underneath that explains it. */
.chart-key .chart-rest {
  flex-basis: 100%;
}

/* The rule that chose which columns are on the chart, under the legend that
   lists them. A size down, like the legend it explains, and not hidden from a
   screen reader the way the legend is: a list of colours is worth nothing
   without them and this sentence is worth the same with or without. */
.chart-hint {
  font-size: max(12px, 0.85em);
  /* The legend's ink rather than the fainter one `.hint` carries everywhere
     else, which is the same token the list above this line is drawn in. The
     order was backwards: a list of colours this product deliberately hides from
     a screen reader was drawn more plainly than the one sentence saying which
     columns are on the chart and why - and that rule is the only thing here a
     reader cannot work out by looking. Both clear their floor either way, so
     this is about which of the two is read first. */
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
}

.chart-swatch {
  /* Its own size whatever the name beside it, which wraps instead. */
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: 2px;
  /* A hairline of the page's own border, so a swatch the same colour as the
     card behind it - which the third round of the palette comes close to - is
     still a square rather than a gap in the list. */
  border: 1px solid var(--ds-border, var(--fallback-border));
}

/* A checkbox rather than a fourth mode: it is not another thing the cells could
   show, it is a question about the ones already there. Set apart from the modes
   by its own box and by the gap, so the group of three still reads as a group
   of three. */
/* The two questions about what is missing, side by side where the room allows
   and stacked where it does not. Both are about the same absence - one marks
   the cells it produced, the other takes away the rows that are nothing else -
   so they read as a pair rather than as a control and another control. The
   wrap is the gadget's own answer: at a 270px box they stack, and the band is
   then two rows tall in the two views that offer both. */
.asks {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--ds-space-150, 12px);
}

.ask {
  display: flex;
  /* WCAG 2.5.8 asks 24 by 24 of a target that is not inline in a sentence.
     Measured at 154 by 20: the row of text was as tall as its text and no
     taller, and this is the one control in the gadget that is a label rather
     than a button in a row of them. */
  min-height: 24px;
  /* Stated, not inherited. The one checkbox in the settings form that also
     carries `.field` took that rule's column direction, and `align-items:
     center` then centred its box above its own label: 51 pixels tall where its
     neighbours are 29, with the box 167 pixels from the left edge of the text
     it belongs to. */
  flex-direction: row;
  align-items: center;
  gap: var(--ds-space-050, 4px);
}

/* The way out of a table that will not fit, under the table it is about. */
.too-wide {
  margin-top: var(--ds-space-100, 8px);
}

/* The count of what is off screen sits directly under the table, above the
   advice about how to make it fit: one says what a reader is looking at, the
   other what to do about it, and that is the order they are needed in. */
.hint.away {
  margin-top: var(--ds-space-100, 8px);
}

/* The space the mark takes, where there is nothing to mark. */
.ask-empty {
  min-height: 24px;
}

/* All four captions in one cell, so the block is as tall as the longest of
   them at this width and the table below it does not move between views. */
.caption-sizer {
  display: grid;
  min-width: 0;
}

.caption-sizer > .grid-caption {
  grid-area: 1 / 1;
}

.caption-sizer > .grid-caption[aria-hidden] {
  visibility: hidden;
}

.mode {
  padding: var(--ds-space-050, 4px) var(--ds-space-100, 8px);
  border-radius: var(--ds-border-radius, 3px);
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
  cursor: pointer;
  user-select: none;
}

/* A view this grid on screen cannot answer, held in its place so the row does
   not move. The disabled text token rather than opacity, so the word stays a
   colour the theme chose; the reason is in its tooltip. */
.mode.off {
  color: var(--ds-text-disabled, var(--fallback-text-subtlest));
  cursor: not-allowed;
}

/* What the plates of where the issues come from say once both hold something:
 * the chosen one is in use, and the other holds what Save will not keep. Muted,
 * both - the radio already says which is chosen, and these only say what that
 * means on Save; the words are in the tooltip and the radio's description. */
.mode .source-in-use {
  margin-inline-start: var(--ds-space-075, 6px);
  font-weight: 400;
  font-size: max(12px, 0.85em);
}

.mode .source-held {
  margin-inline-start: var(--ds-space-050, 4px);
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* Clearing a search: Atlassian's link button, text without a fill, because it
 * is a secondary way out of two boxes and not an answer beside them. At the
 * start of its line, under the sentence that describes it, rather than
 * stretched across the column with its words in the middle. Held to
 * the 24 pixels WCAG 2.5.8 asks of a target, and the padding kept so its focus
 * ring does not sit on the words. */
.clear-source {
  display: block;
  justify-self: start;
  font: inherit;
  min-height: 24px;
  margin-block: var(--ds-space-050, 4px);
  padding: var(--ds-space-025, 2px) 0;
  color: var(--ds-link, var(--fallback-selected-text));
  background: none;
  border: 0;
  cursor: pointer;
}

.clear-source:hover {
  text-decoration: underline;
}

.clear-source:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

.mode.chosen {
  /* Measured in a browser on the preview page: 9.09:1 in the light theme and
     7.87:1 in the dark, against a floor of 4.5. Inside a gadget the host
     supplies the tokens and neither number applies; these are what the local
     fallbacks give, which is what every screenshot goes through. */
  color: var(--ds-text-selected, var(--fallback-selected-text));
  background: var(--ds-background-selected, var(--fallback-selected));
  font-weight: 600;
}

/* The radio itself carries the keyboard behaviour and the screen reader's
   understanding of the group; the label carries the look. Hidden with a clip
   rather than display:none, which would take it out of the tab order. */
/* A radio hides its input and carries the state on the label, because one
   filled plate along a row says "this is the view you are looking at".
   Restricted to radios on purpose: a checkbox that took this had the box
   clipped away and no plate to replace it, so five of them in the settings
   form looked identical on and off while each one changed the number. */
.mode input[type='radio'] {
  position: absolute;
  width: 1px;
  height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
}

/* Every checkbox in this app, and the state it carries.
 *
 * The box itself is the platform's own answer to "is this on", and it is
 * visible for all of them: measured before and after a click, a label that
 * clipped it away kept the same class, background, colour and weight, and the
 * two screenshots were byte for byte identical. On a dashboard column, where
 * most marked cells are outside the visible window, that is a control which
 * changes nothing a reader can see.
 *
 * The label answers the same question a second time, because eleven pixels of
 * box is not much to find in a row of controls - so it starts subtle and
 * thickens into the plain text colour. Both halves are needed and neither is
 * enough: measured, the colour alone was the colour it already inherited. */
.ask input,
.mode input[type='checkbox'] {
  accent-color: var(--ds-background-selected-bold, #1868db);
}

/* The off state, at a specificity the on state can beat.
 *
 * `:has()` takes the specificity of its argument, so
 * `.mode:has(input[type='checkbox'])` scored one class and one attribute - a
 * point above `.mode.asked` below, which therefore never applied. The colour
 * channel was dead in every checkbox in the settings form and nothing said so:
 * the rule was there, it was correct, and it lost.
 *
 * `:where()` contributes nothing, so this is one class and the rule below is
 * two. The selector still matches exactly what it matched before. */
.ask,
.mode:has(:where(input[type='checkbox'])) {
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
}

.ask.asked,
.mode.asked {
  color: var(--ds-text, var(--fallback-text));
  font-weight: 600;
}

/* Both, because the box itself is clipped in the modes and small everywhere
   else: the ring belongs to the label. The checkbox over the grid carries only
   `.ask` and was the one control in the gadget with no focus state of its
   own. */
/* The two buttons the whole form turns on were the only controls in the
   product with no focus ring of their own, so they fell back to the browser's.
   Every other group here - the exports, Keep, the setups, the disclosures, the
   modes, the mark, every figure - has one. */
/* On `:has(:focus-visible)` and not on `:focus-within`, which is the same
   difference every other rule here already makes. `:focus-within` fires for a
   pointer as well as for a keyboard, so clicking the mark left a blue box
   standing around its label - measured in a browser on a real click, the input
   reports `:focus-visible` false and the label was outlined anyway. A ring that
   a mouse can leave behind reads as a control stuck in a state rather than as
   where the keyboard is.

   `:has()` rather than putting the ring on the input: the box is eleven pixels
   and clipped in the modes, so the ring belongs to the label around it. This
   file already depends on `:has()` for the colour of a checked label. */
.actions button:focus-visible,
.exports .copy:focus-visible,
.exports .save-picture:focus-visible,
.change-actions .copy:focus-visible,
.download-open:focus-visible,
.wider:focus-visible,
.series-open:focus-visible,
.series-choosing button:focus-visible,
.mode:has(:focus-visible),
.ask:has(:focus-visible) {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

/* Where the mass sits, read before any figure is.
 *
 * Atlassian's own accent backgrounds rather than an alpha ramp of our own.
 * Two steps rather than three: `subtle` was the deepest until --ds-text on it
 * measured 4.28 against a floor of 4.5 in the dark theme, and `bolder` needs
 * inverted text — a scale whose text colour changes partway cannot be read in
 * one pass.
 *
 * Teal rather than blue, because in this system blue is already spoken for.
 * `background.selected` is `accent.blue.subtlest` — the same bytes, #E9F2FE and
 * #1C2B42 — and `selected.hovered` is `accent.blue.subtler`: the pair this scale
 * used was the pair Jira paints a chosen thing in, and the chosen mode sits a
 * hundred pixels above the grid wearing one of them. A reader who has used Jira
 * reads a pale blue cell as one they picked.
 *
 * Of the nine families that are not blue, this is the quietest in the theme the
 * loudness was in. Measured over all ten: in dark its fills carry 0.027 and
 * 0.057 of OKLCh chroma against blue's 0.047 and 0.095, and nothing between the
 * two exists — the next quietest hue is a third louder again, and the neutral
 * family, at 0.006, is quieter than either but costs three inks. The figure on
 * the fills reads 13.02 and 11.35 in light, 8.76 and 6.73 in dark, and they
 * stand 1.08 and 1.24 off the surface in light, 1.21 and 1.58 in dark.
 *
 * Which is the dark theme's own answer to a problem, inherited here by accident.
 * It has little room to darken a background below a surface already near black,
 * so it separates one by saturating it instead - blue's deep fill carries better
 * than twice the colour of its light-theme twin. On a dashboard that is a grid
 * pulling harder than the thing the reader came to read.
 *
 * Classes rather than an inline style attribute, which would need
 * `unsafe-inline` in the manifest's content policy: a line on the consent
 * screen this app should not have to explain, for a background colour.
 *
 * The colour is redundant. Every shaded cell still carries its number, so a
 * reader who cannot tell the shades apart loses nothing but the glance.
 *
 * The dark block overrides fallbacks only. Inside a gadget the tokens are
 * always defined and always win, so this media query is for `npm run dev`, the
 * preview page and the screenshots taken from them — the one place where the
 * operating system's preference genuinely is the theme. */
.heat-up-1 {
  background: var(--ds-background-accent-teal-subtlest, #e7f9ff);
}

.heat-up-2 {
  background: var(--ds-background-accent-teal-subtler, #c6edfb);
}

/* The second number a figure carries: how many issues it is a sum of.
 *
 * Forty points across four issues and forty across forty are the same total and
 * not the same week, and the reader used to have to change the view and
 * remember what the last one said.
 *
 * Away by default and shown where there is room, rather than the other way
 * round: the narrow dashboard column is the width this product lives at, and a
 * cell there has about 47 pixels for its figure. Measured: the gadget's inner
 * width in a one-column dashboard is 361 px, a status column costs 88-101, and
 * two numbers is the ceiling at that width - three would need a second line.
 *
 * `display: none` rather than a colour or a clip, so a screen reader is not
 * told a number that is not on the screen. What the count says is in the
 * figure's own accessible name either way.
 */
/* One step darker than the rest of this file's secondary text, because this is
   the only secondary text that sits on the colour scale. The two steps of that
   scale were chosen against the main figure - 13.02 and 11.35 in light, 8.76
   and 6.73 in dark - and this number was not in that measurement. On the
   deepest fills it came to 3.79:1 in light and 4.39:1 in dark, five of eight
   pairs under the floor; at worst it is now 5.72:1 in light and 4.55:1 in dark.

   It went unmeasured because it only appears above a container width of about
   392px, and the preview panels the measurements were taken on are 380. */
.beside {
  display: none;
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
  font-variant-numeric: tabular-nums;
  /* A size down, and held off the figure.
     Colour alone was not enough to tell the two apart. Measured on the listing
     frame at full width, a cell read `2970.5 660` and another `3153.5 699`:
     two numbers of similar length, the same size, separated by one space - and
     a reader looking for the total found a six-digit number that is not one.
     The subtler colour said "secondary" only to somebody who already knew
     which of the two was. Smaller says it at a glance, and the gap says the
     two are not one figure. */
  font-size: max(12px, 0.85em);
  margin-inline-start: 0.15em;
}

/* How much a figure has moved since a grid somebody kept.
 *
 * The same slot as the count of issues and not the same rule about width. That
 * count is a second number nobody asked for, so it goes away where two numbers
 * do not fit; this one is the answer to a control the reader has just used, and
 * a control whose effect disappears below a width is a control that does
 * nothing. Only one of the two is ever in a cell.
 *
 * The colour is the subtler one the count uses, one step darker than the rest
 * of this file's secondary text, because this text also sits on the colour
 * scale - measured there at 5.72:1 in light and 4.55:1 in dark, over all eight
 * pairs. The sign is in the digits
 * rather than in a hue: red and green here would collide with the scale the
 * cell is already painted in, and say the opposite thing on a field where less
 * is better. */
.change {
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A difference that moved, which is every one but nought.
 *
 * Plain text colour rather than the subtler one, and a triangle in front of the
 * figure. The sign carried the direction correctly and did not carry it far: a
 * `+` is one glyph the width of a hyphen, in the colour of the digits, beside a
 * figure two or three times as long - and the owner, looking at a real
 * comparison, could not tell at a glance which way a cell had gone.
 *
 * Still no hue. The cell is already painted with the fill scale, so a second
 * colour lands on top of the first, and on a field where less is better green
 * for a rise says the opposite of what is true. The triangle says which way the
 * arithmetic went; whether that is good news is a fact about the team, and this
 * gadget does not know it. */
.change.went-up,
.change.went-down {
  color: var(--ds-text, var(--fallback-text));
}

/* The mark itself, a size down from the digits it stands beside: at the same
   size a filled triangle is heavier than any numeral and reads as the figure. */
.change [aria-hidden='true'] {
  font-size: max(12px, 0.85em);
  margin-inline-end: 0.15em;
}

/* The second figure appears where two numbers fit, and the cap does not move.
 *
 * Both were behind this query, and together they made the gadget show less at
 * 392 pixels than at 391. Measured in a browser on a real board: the table goes
 * from 924 to 1107 pixels while the window grows by one, and fully readable
 * columns fall from three to one. With the axes transposed it is sharper still
 * - the whole grid fits at 391 and 28% of it is off screen at 392 - and it does
 * not come back until about 600.
 *
 * The cap stays the same at every width. Two lines of twelve is
 * twenty-four characters, which is what tells `Ready for Development` from
 * `Ready for Grooming`; the eighteen bought a longer first line and cost two
 * of the three columns a reader had. The second figure stays behind the query, because it is
 * a number nobody asked for and the cell genuinely has room for it there. */
/* The key to that second figure, under the table, drawn exactly where the
   figure is: away by default and shown by the same query, so a narrow card
   carries neither and a wide one carries both. */
.beside-key {
  display: none;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
}

@container gadget (min-width: 360px) {
  .beside {
    display: inline;
  }
  .beside-key {
    display: flex;
  }
}

/* Half the padding beside a figure where the card is a narrow dashboard
 * column, and the same half on the margin row that lines up with the icons.
 *
 * Measured in a browser at 346 pixels with the three status categories across
 * the top: the table needs 412 pixels against a window of 314, and eighty of
 * them are this padding, eight a side on five cells. Four a side gives forty
 * back, which takes the fully readable columns from one to two with epics or
 * teams down the side. Behind the same query as the second figure, so the
 * table does not change shape at any width the query does not already change
 * it at. */
@container gadget (width < 360px) {
  .grid th,
  .grid td {
    padding-inline: var(--ds-space-050, 4px);
  }
  .grid th.past-a-glyph {
    padding-inline-start: calc(
      var(--ds-space-050, 4px) + 16px + var(--ds-space-050, 4px)
    );
  }
}

/* The mark for a position that carries no difference, for any of its reasons.
 *
 * Drawn blank, such a cell read as unchanged beside neighbours saying 0 - which
 * is the opposite of what it means. A dash is not a figure and cannot be
 * mistaken for one, and it takes the subtler colour so it does not read as
 * data either.
 *
 * `subtle` and not `subtlest`, which the comment said and the code did not: one
 * word, and that word is the whole of the decision. The dash falls on a filled
 * cell like everything else in that slot, and measured in a browser on the
 * deepest two fills the subtlest came to 3.83:1 in light and 4.39:1 in dark -
 * under the floor of 4.5, and the only text in the grid that was. Its
 * neighbours in the same slot were moved to this token for exactly this reason
 * and it was not. */
.change.no-step {
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
}

/* The sign, where colour cannot carry it.
 *
 * In the light theme the two halves of the scale are within a twentieth of one
 * luminance - the measurement is beside the hatch below, taken against the
 * published token values - so up and down are told apart by hue and by nothing
 * else there. That is one man in twelve who cannot read the grid at all, and
 * every greyscale print of it, which is where one reader said the report goes:
 * to a board meeting.
 *
 * Three channels, in the order a reader meets them. The digits carry the minus
 * themselves. This is the second: a rule under the figure, from the middle of
 * the cell outwards, left for below nought and right for above. Hue is the
 * third and stays as it was.
 *
 * Drawn from the same class the fill uses, so it says exactly what the fill
 * says and cannot drift from it, and as a background on the figure rather than
 * as an element of its own: anything positioned in a cell paints over the
 * frozen column beside it, which is a defect this grid has already had once.
 *
 * Only on a grid that has both signs in it. A board of story points has one
 * direction, and a bar under every cell would be ink saying nothing. */
.grid.diverging td[class*='heat-'] .drill,
.grid.diverging td[class*='heat-'] .figure {
  background-repeat: no-repeat;
  background-position: bottom;
  background-size: 100% 2px;
}

.grid.diverging .heat-up-1 .drill,
.grid.diverging .heat-up-1 .figure {
  background-image: linear-gradient(
    to right,
    transparent 50%,
    color-mix(in srgb, currentColor 45%, transparent) 50% 64%,
    transparent 64%
  );
}

.grid.diverging .heat-up-2 .drill,
.grid.diverging .heat-up-2 .figure {
  background-image: linear-gradient(
    to right,
    transparent 50%,
    color-mix(in srgb, currentColor 45%, transparent) 50% 82%,
    transparent 82%
  );
}

.grid.diverging .heat-down-1 .drill,
.grid.diverging .heat-down-1 .figure {
  background-image: linear-gradient(
    to right,
    transparent 36%,
    color-mix(in srgb, currentColor 45%, transparent) 36% 50%,
    transparent 50%
  );
}

.grid.diverging .heat-down-2 .drill,
.grid.diverging .heat-down-2 .figure {
  background-image: linear-gradient(
    to right,
    transparent 18%,
    color-mix(in srgb, currentColor 45%, transparent) 18% 50%,
    transparent 50%
  );
}

/* The other side of a diverging scale, for a field that can go below nought.
 *
 * Hatched, and that is the part that carries the direction. Measured against the
 * published token values rather than our fallbacks: in the light theme
 * `teal.subtlest` and `red.subtlest` differ in luminance by a ratio of 1.054 and
 * the deeper step by 1.082 - near enough an isoluminant pair, which is one
 * colour for deuteranopia and very nearly one in greyscale print.
 *
 * That is not a bad choice of ours to correct. Every `subtlest` and `subtler`
 * step in the system is tuned to the same luminance so that text reads the same
 * on all of them, and all ninety ordered pairs of the ten accent families were
 * checked: none separates the two sides by 1.25 or more on both steps in both
 * themes while keeping text above 4.5. Moving red one step deeper comes closest
 * and drops text on it to 4.19 in dark, which is the same wall that already
 * removed the third step.
 *
 * So the direction is carried by texture, which survives colour blindness, a
 * black and white printer and a photocopy. The hatch is drawn from the text
 * colour, so it darkens a light theme and lightens a dark one without a second
 * palette. The number in the cell already carries its own minus sign; this is
 * what makes a field of them readable at a glance.
 *
 * The dark theme separates them further, and the rising side is the lighter:
 * 1.14 apart at the shallow step and 1.50 at the deep one. That helps a reader
 * who sees no hue and costs a little symmetry - a fall of fifty does not read
 * quite as heavy there as a rise of fifty. What holds the magnitude honest is
 * the rule under the figure, which is drawn from the same class on both sides
 * and is the same length for the same number either way. */
.heat-down-1,
.heat-down-2 {
  background-image: repeating-linear-gradient(
    -45deg,
    color-mix(in srgb, currentColor 10%, transparent) 0 1px,
    transparent 1px 6px
  );
}

.heat-down-1 {
  background-color: var(--ds-background-accent-red-subtlest, #ffeceb);
}

.heat-down-2 {
  background-color: var(--ds-background-accent-red-subtler, #ffd5d2);
}

@media (prefers-color-scheme: dark) {
  .heat-up-1 {
    background: var(--ds-background-accent-teal-subtlest, #1e3137);
  }

  .heat-up-2 {
    background: var(--ds-background-accent-teal-subtler, #164555);
  }

  .heat-down-1 {
    background-color: var(--ds-background-accent-red-subtlest, #42221f);
  }

  .heat-down-2 {
    background-color: var(--ds-background-accent-red-subtler, #5d1f1a);
  }
}

/* Windows High Contrast, where a background colour is not a channel at all.
 *
 * Forced colours replace every background and remove every background-image, so
 * the four depths of the scale and the hatch that marks the falling half of a
 * diverging one all arrive as one flat surface. The figures survive - the fill
 * is redundant by design and every shaded cell carries its number - but the
 * glance does not, and two things that were never redundant went with it.
 *
 * The rank is drawn again in a channel forced colours keep: an inset outline,
 * whose width says how deep the fill was. Widths rather than colours, and
 * inset rather than a border, so nothing in the table moves.
 *
 * And the chosen view, which was distinguished by a background and a heavier
 * weight over a radio clipped to a single pixel: in forced colours that left
 * `Total` and `Issues` looking alike, over a control whose whole point is that
 * one of the four is picked. */
@media (forced-colors: active) {
  .heat-up-1,
  .heat-down-1 {
    outline: 1px solid;
    outline-offset: -1px;
  }

  .heat-up-2,
  .heat-down-2 {
    outline: 3px solid;
    outline-offset: -3px;
  }

  .mode.chosen {
    outline: 2px solid;
    outline-offset: -2px;
  }
}

/* Every figure opens the issues behind it.
 *
 * A button rather than a link, because there is no href to give it: the gadget
 * does not know which site it is on, and Forge's router is what resolves a path
 * against the host.
 *
 * Styled as text, not as a control. A grid where every cell is a button reads
 * as a form; the affordance arrives on hover and on focus, which is when the
 * reader is asking for it. */
.drill,
.figure {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  border-radius: var(--ds-border-radius, 3px);
}

/* Filling its cell, which is a thing only a figure in the table does.
 *
 * Measured at 8.9 by 18.5 pixels with no padding, against the 24 by 24 that
 * WCAG 2.5.8 asks for. The cell around it is 60 by 32, so the button can fill
 * it: padding rather than a stretched `td`, because a cell with no issues
 * deliberately has no button and clicking its empty half would otherwise open
 * a search that returns nothing while looking like it worked.
 *
 * Scoped to the table. Unscoped, the caveat button under the table took it too
 * - and the most important sentence in that note was drawn as a full-width
 * block flushed right, under three sentences flushed left. */
.grid .drill,
.grid .figure {
  display: block;
  width: 100%;
  min-height: 24px;
  padding: var(--ds-space-050, 4px) 0;
  text-align: right;
}

/* A sentence, in the flow of the sentences around it.
 *
 * Padding to nothing as well. `.drill` gives up the border and the background a
 * button comes with and left its padding alone, which is right inside a cell -
 * there the rule above sets its own - and wrong here: the browser's own few
 * pixels sat in front of the first word, so the one caveat that is a button
 * began a hair further in than the sentences above and below it and read as a
 * list item that had lost its bullet. Found by the owner on a real install. */
.hint .drill {
  display: inline;
  padding: 0;
  text-align: inherit;
  /* Underlined at rest, where a figure in the table is underlined only under
     the pointer. A sentence in a paragraph of sentences has nothing but this
     to tell a reader it can be pressed at all - the owner, reading it on a
     real install, said he would not have guessed - and there is exactly one of
     these under a table, where figures come by the dozen.

     And in the plain text colour, a step up from the caveats around it: it is
     something to do rather than something to know. */
  color: var(--ds-text, var(--fallback-text));
  text-decoration: underline;
  text-decoration-color: currentColor;
}

/* The affordance, on the figures that have something behind them. The pooled
   row does not: `Everything else` is this app's word for the values it did not
   draw, and no query names it.

   Under the pointer, and not before it. A line under every figure is a line
   under every cell of a table whose whole content is figures, and at that
   density it stops reading as an affordance and starts reading as ruling: the
   owner, looking at a grid of them on a real install, said it was noise.

   What carries the affordance instead is what every reader already has. The
   pointer turns and the line arrives together under it; a keyboard lands on
   the focus ring below; and the accessible name of every figure ends with what
   pressing it does, which is the one of the three that was never the weakest.
   The sentence under the table keeps its line at rest, because there is one of
   those in a paragraph of prose rather than one in every cell. */
.drill {
  cursor: pointer;
  text-decoration: none;
  text-underline-offset: 3px;
}

.drill:hover {
  text-decoration: underline;
  text-decoration-color: currentColor;
}

/* A nought, which is a figure, against a blank, which is the absence of one.
 *
 * On the screen the two differed by the presence of a character and by nothing
 * else: a cell drawing 0 with four issues behind it sat beside a cell that is
 * blank because it holds none. The subtler colour says which kind of statement
 * this is without taking the figure away.
 *
 * Measured against the card, which is the only surface a nought sits on:
 * 5.08:1 in the light theme and 5.79:1 in the dark, against a floor of 4.5.
 * `scaleFor` leaves nought out of the scale deliberately, so this colour never
 * lands on a fill - where the same token gives 3.83:1 and 4.39:1. */
.drill.nought,
.figure.nought {
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* A figure that part of its population did not contribute to.
 *
 * A corner mark rather than a second number: the cell has about forty-seven
 * pixels of room and the figure needs all of them, so the count of issues
 * carrying no value is said in the accessible name and on hover instead. This
 * is the flag that sends somebody there to read it.
 *
 * On every figure that left an issue out, not on a share of them. There was a
 * threshold here - half or more - and it was removed because a reader who asks
 * the question wants every figure that left something out, however few. The
 * cost is measured and real: on a board where nearly everything is partly
 * unestimated the mark lands on 38 of 46 figures, four in five, which is the
 * wallpaper a threshold prevents. Whether it comes back is CRS-151; what this
 * file must not do is argue for a rule it does not run.
 *
 * Never the only carrier of the fact - the sentence under the grid gives the
 * quantity for the whole table, and the accessible name gives it per figure
 * whatever is missing, so a reader who cannot see the mark still gets the
 * number. */
.drill.understated,
.figure.understated {
  position: relative;
}

.drill.understated::after,
.figure.understated::after {
  content: '';
  position: absolute;
  top: 1px;
  right: 0;
  border-top: 5px solid var(--ds-icon-warning, var(--fallback-warning));
  border-left: 5px solid transparent;
}

/* The key to that mark, under the table: the mark itself in front of the
   sentence that says what it means, in the corner of a figure-sized square
   where the grid draws it - a sign for the sentence's kind would stand for
   something else. Spaced from its sentence like every other sign there. */
.mark-key {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-075, 6px);
}

.mark-swatch {
  position: relative;
  flex: none;
  width: 12px;
  height: 12px;
  margin-top: 0.22em;
}

.mark-swatch::after {
  content: '';
  position: absolute;
  top: 1px;
  right: 0;
  border-top: 5px solid var(--ds-icon-warning, var(--fallback-warning));
  border-left: 5px solid transparent;
}

.drill:focus-visible,
.figure:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 2px;
}

/* The caption, outside the scrolling box.
 *
 * The one that is showing is a heading - there was no heading anywhere in this
 * product, so a reader moving by heading had nothing to move to - and the other
 * three are the same box with the words hidden, holding the block's height. The
 * face is stated rather than inherited for exactly that reason: a heading that
 * came in at its browser size would make the block as tall as itself. */
.grid-caption {
  margin: 0 0 var(--ds-space-100, 8px);
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
  font-size: inherit;
  font-weight: inherit;
  /* A search's name is the reader's, and one word of it can be wider than
     the gadget; it wraps inside rather than running past the edge. */
  overflow-wrap: anywhere;
}

/* Said and not drawn.
 *
 * For the one sentence that tells a reader what the whole table is now about.
 * `display: none` and `visibility: hidden` both take it out of the accessible
 * tree, which is the opposite of the point, so it is a one-pixel box clipped to
 * nothing - the pattern every screen-reader-only class uses, written out here
 * rather than pulled in. */
.only-said {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The table as text, for a reader whose clipboard this browser will not let us
   write. Monospaced and pre-wrapped, because what is in it is tab-separated
   columns and a proportional face turns them into a paragraph; the full width
   of the card, because the reader is about to select all of it anyway. */
.by-hand {
  display: block;
  width: 100%;
  margin-top: var(--ds-space-050, 4px);
  box-sizing: border-box;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  white-space: pre;
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-sunken, var(--fallback-neutral));
  border: 1px solid var(--ds-border, var(--fallback-border));
  border-radius: 3px;
}

/* Two destinations for the same grid, and a third that stays here. Under the
   table, because a reader takes it away after reading it rather than before.
 *
 * Wrapping, because the sentence beside the buttons appears when one is pressed
 * and is as long as a sentence: without it the buttons were squeezed instead,
 * their labels broke over two lines, and one press of Download doubled the
 * height of the row and pushed the rest of the gadget down. */
.exports,
.keeps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-100, 8px);
  margin-top: var(--ds-space-100, 8px);
}

/* One rule for all of them, so every control in these rows is the same size.
   The comment here used to say "all three" and the selector listed only
   `button`, so the two selects in the Keep row - which grid to show, and which
   to compare with - inherited nothing at all: measured in a browser, 19 pixels
   tall in the browser's own 13.33px Arial, beside a 30 pixel button in the
   app's face. Under the 24 by 24 WCAG 2.5.8 asks, and the only two controls in
   the gadget that were. The rule that documented the defect is the one that
   let it back in on the next control added to the row. */
.exports .copy,
.exports .save-picture,
.change-actions .copy,
.download-open,
.wider,
.draw-as,
.keeps button,
.series-open,
.series-close,
.series-choosing button {
  white-space: nowrap;
  font: inherit;
  padding: var(--ds-space-050, 4px) var(--ds-space-100, 8px);
  color: var(--ds-text, var(--fallback-text));
  background: none;
  border: 1px solid var(--ds-border-input, #8590a2);
  border-radius: var(--ds-border-radius, 3px);
  cursor: pointer;
}

/* One list of kept grids, where both choices are made.
 *
 * Two unlabelled selects side by side were two lists of dates, and which of
 * them decided what is drawn and which decided what it is measured against was
 * a thing the reader had to remember - then build again the next morning,
 * because it did not survive a reload.
 *
 * A height of its own, so that nothing under it moves while the reader is
 * choosing: the panel scrolls inside itself rather than growing with the list,
 * and a fifty-row history and a two-row one take the same room. Measured on a
 * 346 pixel column, five rows is what fits without covering the whole card.
 *
 * The button sits on the row beside `Keep this`, where it belongs: it is one
 * more control and it is shorter than the one next to it.
 *
 * The wrapper is not a box at all. `display: contents` puts the button and the
 * panel straight into the row, so the button is one flex item beside `Keep
 * this` and the panel is another that claims a line of its own - which means
 * the button does not move when it is pressed, and the panel is still the
 * width of the card.
 *
 * In the flow, and that is the whole of the reason it is written this way.
 * Taken out of it - absolutely positioned over the table, which is what the
 * pass that asked for this panel expected - the panel drew below the bottom
 * edge of the gadget's iframe, which Forge sizes to the flow content: measured
 * in Jira, the two buttons under the list were outside the frame and the
 * reader could choose a pair with no way of applying it. A page would have
 * overlaid; an auto-sized iframe has nothing there to overlay onto. */
.kept-grids {
  display: contents;
}

/* The two controls here whose labels are sentences rather than words, let off
   the rule that keeps a label on one line. At 346 pixels `Kept grids (1) and
   1 other configuration elsewhere` and `Close the list of kept grids` are each
   wider than the card, and on one line they ran out past its edge. The row
   still breaks before them - it measures a control at its one-line width - so
   the label only wraps where the control has a line to itself and still does
   not fit. */
.kept-grids .kept-open,
.kept-grids .kept-close {
  white-space: normal;
  max-width: 100%;
  text-align: start;
}

/* What the panel says about time: how long ago the last grid was kept, and how
   far back a full history reaches at this reader's rhythm.
 *
 * A line each, because the wrapper above is `display: contents` and these are
 * therefore flex items of the row the buttons are in. Without the basis they
 * sat beside `Keep this` and wrapped mid-sentence at every width the gadget
 * meets on a dashboard. The line saying whether the grid on screen is the
 * one already kept, and the fold after it, for the same reason: at page width
 * they ran into one line with the count of kept grids after them, and the
 * count read as part of the fold's title. */
.kept-time,
.kept-moved,
.kept-why,
.kept-reach {
  flex-basis: 100%;
  min-width: 0;
}

.kept-panel {
  flex-basis: 100%;
  min-width: 0;
  background: var(--ds-surface-overlay, var(--ds-surface, var(--fallback-surface)));
  border: 1px solid var(--ds-border, #8590a244);
  border-radius: var(--ds-border-radius, 3px);
  /* The list scrolls and the two buttons under it do not.
   *
   * Measured on a real board: with the whole panel scrolling, a history under
   * other settings pushed `Show these` past the ceiling and out of sight, and
   * on macOS - where overlay scrollbars are the default - there was no
   * scrollbar at rest to say so. The reader could choose a pair and had no way
   * of applying it that they could see. */
  display: flex;
  flex-direction: column;
  /* Tall enough to hold rows rather than parts of them.
   *
   * At 15em, measured in a 297 px dashboard column: the panel came to 212 px,
   * its heading and the table's own headings took 126 of them, and the 86 px
   * left was less than half of one row. A reader picking what to compare
   * against was choosing between a moment they could read and one they could
   * not, which is a mis-click on the control this panel exists for. */
  max-height: 26em;
  overflow: hidden;
}

.kept-scroll {
  overflow: auto;
  min-height: 0;
}

.kept-panel-name {
  margin: 0;
  padding: var(--ds-space-100, 8px);
  font-weight: 600;
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
}

.kept-list {
  width: 100%;
  border-collapse: collapse;
}

/* The two words at the top of the two columns of radios, which are the whole
   of what says which choice is which. Small, because they are read once and
   then recognised.

   `nowrap` is for those two, and it reached the third heading as well, which is
   not a word but a sentence. Measured in a dashboard column: the table asked
   for 412 px inside a 297 px window, so the list scrolled sideways, and the
   delete control sat 91 px past the right edge of the panel - the one action in
   this list a reader cannot reach by scrolling down, because the scroll that
   reveals it is the one nothing says is there. */
.kept-list th {
  text-align: left;
  font-weight: 600;
  padding: var(--ds-space-050, 4px) var(--ds-space-100, 8px);
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
  white-space: nowrap;
}

/* The two words that say which column is which, kept on screen while the list
   scrolls under them. They are read once and then recognised, and a reader who
   has scrolled to the fourth moment is exactly the reader who has lost them. */
.kept-list thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--ds-surface-overlay, var(--ds-surface, var(--fallback-surface)));
}

/* The heading that is a sentence, let off the rule written for the two words.
   It costs the heading row a second line and takes the table to 297, which is
   the width it was given: the delete control comes back inside the panel and
   the sideways scroll goes. */
.kept-list th.kept-when {
  white-space: normal;
}

.kept-list td {
  padding: var(--ds-space-050, 4px) var(--ds-space-100, 8px);
  vertical-align: top;
}

/* The two columns of radios, kept to the width of a radio.
 *
 * They are headed by two words and the words were setting the width: `Show`
 * and `Against` took 120 px of the 248 a dashboard column leaves this table,
 * so the moment and what the grid holds shared 88 - and wrapped to eight lines
 * of a 202 px row. The headings are what this narrows, not the targets: the
 * radio keeps its own hit area, and the cell is centred on it.
 */
.kept-list :is(th, td):is(:first-child, :nth-child(2)) {
  width: 2.5em;
  padding-inline: var(--ds-space-050, 4px);
  text-align: center;
}

/* The row being shown, marked so the reader can find it again in a list of
   fifty without reading the dates. */
.kept-row.kept-shown {
  background: var(--ds-background-selected, var(--fallback-selected));
}

.kept-when {
  width: 100%;
}

.kept-moment {
  display: block;
}

/* What the grid holds, which is how one snapshot is told from the one above it
   before either is opened. Subtler than the moment, because it is the second
   thing read and not the first. */
.kept-holds {
  display: block;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* The name somebody types, which is what stops twenty timestamps being noise.
   Bordered only when it has focus or something in it: fifty empty boxes in a
   column is a form, and this is a list. */
.kept-name {
  font: inherit;
  width: 100%;
  min-width: 0;
  padding: var(--ds-space-025, 2px) 0;
  color: var(--ds-text, var(--fallback-text));
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
}

.kept-name:focus-visible,
.kept-name:not(:placeholder-shown) {
  border-bottom-color: var(--ds-border-input, #8590a2);
}

/* Big enough to hit. Measured in a browser, the delete button came out 18
   pixels wide against the 24 WCAG 2.5.8 asks for - the radios beside it are
   the browser's own and are covered by that rule's user-agent exception, but
   this one is styled here and so is ours to get right. */
.kept-list button {
  font: inherit;
  min-width: 24px;
  min-height: 24px;
  padding: var(--ds-space-025, 2px) var(--ds-space-050, 4px);
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
  background: none;
  border: 1px solid transparent;
  border-radius: var(--ds-border-radius, 3px);
  cursor: pointer;
}

.kept-list button:focus-visible,
.kept-name:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

.kept-doing {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  gap: var(--ds-space-100, 8px);
  padding: var(--ds-space-100, 8px);
  border-top: 1px solid var(--ds-border, #8590a244);
}

/* The line saying a choice is not on the grid yet, on a row of its own. At the
   width a dashboard column gives, a sentence beside three buttons leaves no
   room for either. */
.kept-not-shown {
  flex-basis: 100%;
  margin: 0;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* At the end of the row, so a cross stays where it was when another row's
   question widens the column. Started from the left, every cross moved across
   with that column's left edge - 186 px at page width - and the reader aiming
   at the next row's cross found it somewhere else; the column's far edge is
   the table's, and nothing moves it. */
.kept-doing-one {
  white-space: nowrap;
  text-align: end;
}

/* Refusing, and saying so by looking like it. Measured: the button that will
   not act had the same text colour, the same border and the same pointer as
   the one that will, so it was pressed twice and nothing happened either time.
   The export buttons never refuse, so only Keep's are here. */
.keeps button[aria-disabled='true'] {
  color: var(--ds-text-disabled, var(--fallback-disabled));
  border-color: var(--ds-border-disabled, #8590a244);
  cursor: default;
}

/* Not on a control that is refusing. The hover rule sat after the refusing
   rule at equal specificity and overrode it, so `Keep this` lit up under the
   pointer exactly as the working `Copy` beside it did - measured, the same
   rgba(9, 30, 66, 0.06) on both, with only the cursor telling them apart. */
.exports button:not([aria-disabled='true']):hover,
.keeps button:not([aria-disabled='true']):hover {
  background: var(--ds-background-neutral, var(--fallback-neutral));
}

.exports button:focus-visible,
.keeps button:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

/* Two rows under the table, and a gap between them wider than the gap inside
   either: the one that hands the grid somewhere else, and the one that keeps
   and publishes it. With one gap everywhere the eight buttons read as one
   block, which is what the owner meant by the buttons running together. */
/* Its own line under the Keep row: a choice about the future, apart from the
   presses about the grid on screen. */
.keep-automatically {
  flex-basis: 100%;
}

/* What a site without a subscription can still remove, under the sentence
   saying so and inside its card. Its sentences are the panel's own rather than
   the warning's, so they keep the panel's colour, and each kept grid is one
   line: when it was kept, and the cross that deletes it. */
.note-warn .without-subscription .hint {
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

.without-subscription .kept-to-remove {
  flex-basis: 100%;
}

.without-subscription ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.without-subscription li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-100, 8px);
  margin-top: var(--ds-space-050, 4px);
}

.without-subscription .kept-moment {
  display: inline;
}

.exports + .keeps {
  margin-top: var(--ds-space-200, 16px);
}

/* A mark beside a button's word, drawn the way the marks beside the sentences
   are and laid on the word's own line rather than on the first line of a
   paragraph - so no nudge down. The word stays; the mark is what lets the row
   be read by shape before it is read by word. */
.with-sign {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-050, 4px);
}

.with-sign .sign {
  margin-top: 0;
}

/* The one filled button under the table, and the one the rest of the row
   follows from - a grid has to be kept before it can be compared, followed or
   published. The fill Keep already has in Crosstab Change, so the same press
   looks the same in both gadgets. The border stays, in the fill's colour, so
   this button is exactly as tall as the framed ones beside it. */
.keeps button.keep-this {
  color: var(--ds-text-inverse, #ffffff);
  background: var(--ds-background-brand-bold, #1868db);
  border-color: var(--ds-background-brand-bold, #1868db);
}

.keeps button.keep-this:not([aria-disabled='true']):hover {
  background: var(--ds-background-brand-bold-hovered, #1558bc);
}

/* Refusing, and filled with nothing - the same channel `.actions` uses for a
   refusing primary. The rule above for a refusing button in this row sets the
   colour and the border and would have left the fill blue under grey text. */
.keeps button.keep-this[aria-disabled='true'] {
  background: var(--ds-background-disabled, transparent);
  border-color: var(--ds-border-disabled, #8590a244);
}

/* Taking a published grid down, drawn as a quiet link. It undoes the press
   beside it, and a second framed button in the row was a second thing of the
   weight of Keep. Underlined, because an unframed word that acts has to say
   so; padded like its neighbours, so it is still a 24 pixel target. */
/* The room Publish will take while the site has not yet said whether
   publishing is on: nothing to see or press, and the controls after it stay
   where they will be once it is drawn. */
.keeps button.publish-pending {
  visibility: hidden;
}

.keeps button.withdraw {
  border-color: transparent;
  color: var(--ds-text-subtle, var(--fallback-text-subtle));
  text-decoration: underline;
}

.keeps button.withdraw:not([aria-disabled='true']):hover {
  background: none;
  color: var(--ds-text, var(--fallback-text));
}

/* The count of kept grids on a dashboard, which is the way to them: the gadget
   draws no list, and this opens the page that does. Drawn as words that lead
   somewhere - the sentence a reader already pressed on, underlined - rather
   than as a second framed button beside Keep. Padded like its neighbours, so
   it is still a 24 pixel target. Changes since beside it is drawn the same
   way, for the same reason: it leads to the page as well. */
.keeps button.kept-to-page,
.keeps button.changes-since {
  border-color: transparent;
  color: var(--ds-link, var(--fallback-selected-text));
  text-decoration: underline;
  white-space: normal;
  text-align: start;
}

.keeps button.kept-to-page:hover,
.keeps button.changes-since:hover {
  background: none;
}

/* The three files, in a list over the button rather than under it.
 *
 * Anchored to the row, not to the button: the list carries a sentence about
 * what the files hold, it is wider than Download, and a list anchored at the
 * button's left edge ran past the right edge of a 346 pixel column - which in
 * a frame is either a cut list or a sideways scroll on the whole card. The row
 * is the width of the card, so the list can never be wider than the card.
 *
 * Upwards, because a gadget's frame is sized to what is in the flow and this
 * row is the last of it: a list opening downwards is drawn below the bottom of
 * the frame. Above the row there is always the table. */
.exports {
  position: relative;
}

.download {
  display: contents;
}

.download-list {
  position: absolute;
  bottom: 100%;
  left: 0;
  z-index: 2;
  box-sizing: border-box;
  width: min(100%, 22rem);
  margin-bottom: var(--ds-space-050, 4px);
  padding: var(--ds-space-050, 4px) 0;
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-overlay, var(--fallback-surface));
  border: 1px solid var(--ds-border, var(--fallback-border));
  border-radius: var(--ds-border-radius, 3px);
  box-shadow: var(
    --ds-shadow-overlay,
    0 8px 12px rgba(9, 30, 66, 0.15),
    0 0 1px rgba(9, 30, 66, 0.31)
  );
}

.download-list[hidden] {
  display: none;
}

.download-list .what-these-hold {
  margin: 0;
  padding: var(--ds-space-050, 4px) var(--ds-space-150, 12px);
}

.download-list [role='menuitem'] {
  display: block;
  width: 100%;
  font: inherit;
  text-align: start;
  padding: var(--ds-space-075, 6px) var(--ds-space-150, 12px);
  color: var(--ds-text, var(--fallback-text));
  background: none;
  border: none;
  cursor: pointer;
}

.download-list [role='menuitem']:hover,
.download-list [role='menuitem']:focus {
  background: var(--ds-background-neutral-subtle-hovered, var(--fallback-neutral));
}

.download-list [role='menuitem']:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: -2px;
}

/* One cell, followed across the grids kept of it.
 *
 * The same shape as the picker above it and for the same reason: `display:
 * contents` puts the button into the row of controls beside `Keep this` and
 * lets the panel claim a line of its own, so pressing it moves nothing. In the
 * flow rather than over the table, because Forge sizes a gadget's iframe to its
 * flow content and anything positioned over it draws below the bottom edge -
 * measured once already, with the buttons of the kept-grids panel outside the
 * frame and unreachable. */
.series {
  display: contents;
}

.series-panel {
  flex-basis: 100%;
  min-width: 0;
  background: var(--ds-surface-overlay, var(--ds-surface, var(--fallback-surface)));
  border: 1px solid var(--ds-border, #8590a244);
  border-radius: var(--ds-border-radius, 3px);
}

.series-name {
  margin: 0;
  padding: var(--ds-space-100, 8px);
  font-weight: 600;
  color: var(--ds-text-subtle, var(--fallback-text-subtlest));
}

.series-what {
  margin: 0;
  padding: 0 var(--ds-space-100, 8px) var(--ds-space-100, 8px);
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* The choices and the two buttons on one row, wrapping rather than scrolling.
 * A 346 pixel column takes two of these at a time, and a select pushed off the
 * side of a card is a control that does not exist. */
.series-choosing {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-100, 8px);
  align-items: end;
  padding: 0 var(--ds-space-100, 8px) var(--ds-space-100, 8px);
}

/* A label above its select rather than beside it: two labelled selects side by
   side on a narrow column leave the select two characters wide. */
.series-choice {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-025, 2px);
  min-width: 0;
}

.series-choice label {
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

.series-choice button.choose[role='combobox'] {
  max-width: 14em;
}

/* A table the panel can scroll rather than clip, for the case the wrapping
   below cannot fix: a very long moment, or a browser whose date format is
   wider than this one's. Nothing is ever cut off the right edge; at worst it
   is behind a scrollbar the reader can move. */
.series-scroll {
  overflow-x: auto;
}

.series-list {
  width: 100%;
  border-collapse: collapse;
}

.series-list th,
.series-list td {
  padding: var(--ds-space-050, 4px) var(--ds-space-100, 8px);
  text-align: left;
  vertical-align: top;
}

/* The two headings wrap and the rows below them do not, which is the other
   half of what makes this table fit a dashboard column. Measured on the stand
   at 312 pixels: held on one line it wanted 473 and the right-hand column was
   cut off the edge - and a column whose heading reads `Si` has no heading. The
   moments underneath are the floor and they are 162 px, so the wrapping is
   where the rest has to come from. */
.series-list thead th {
  font-weight: 600;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* The moment, and under it whatever somebody typed on that grid. The name is
   the one part of a snapshot written on purpose, and a column of twelve
   timestamps differing by a few characters is what it exists against. */
.series-list tbody th {
  font-weight: 400;
  white-space: nowrap;
}

/* The name somebody typed, which can be as long as they liked. The row it sits
   in holds its moment on one line; held on one line itself, a name of forty
   characters put the whole panel into horizontal scroll at a dashboard's
   width - and that scroller is not reachable from a keyboard. It is the one
   thing in this table that is a caption rather than a figure, so it wraps. */
.series-named {
  display: block;
  white-space: normal;
  overflow-wrap: anywhere;
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

.series-figure {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The difference beside its figure, in the same cell, which is how the table
   above this one draws one. It gets a space of its own and nothing more: the
   `.change` rules beside it already decide the colour, the mark and the
   direction. */
.series-move {
  margin-inline-start: 0.5em;
}

/* The figure, a space clear of the mark in front of it. The rule that shrinks
   a difference's triangle puts that space after the mark instead, and reaches
   only what a comparison hides from a screen reader - which this figure is
   not, so the space has to be put back on this side of it. */
.series-move .series-seen {
  margin-inline-start: 0.15em;
}

/* What the table is of, seen rather than only read out. The selects above can
   be moved without moving it, so a reader can tell at a glance whether the
   figures under it are of the cell they are now looking at. */
.series-list caption {
  padding: 0 var(--ds-space-100, 8px) var(--ds-space-050, 4px);
  text-align: left;
  font-weight: 600;
  color: var(--ds-text, var(--fallback-text));
}

/* The same corner mark the grid puts on an understated figure, and here for
   the same fact: the read behind this figure stopped early. It carries no
   text, so selecting the table copies the figures and not the word. */
.series-partial {
  display: inline-block;
  width: 0;
  height: 0;
  margin-inline-start: 0.4em;
  vertical-align: top;
  border-top: 5px solid var(--ds-icon-warning, var(--fallback-warning));
  border-left: 5px solid transparent;
}

/* Why a figure is not here, which is one of four things and never nothing at
   all - the board, the view, or a read that stopped short - so it is a phrase
   and not a blank. */
.series-absent {
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

.series-said {
  margin: 0;
  padding: var(--ds-space-100, 8px);
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
}

/* A reading that was refused, which is the one thing this panel says that must
   not look like the explanations under the table. Written in the same grey as
   those, it read as a footnote about the feature rather than as the answer to
   the button just pressed. */
.series-wrong {
  color: var(--ds-text-warning, var(--fallback-warning));
}

/* The press that reads on from where a read stopped.
 *
 * Inside the sentence rather than under it, for the reason `.limit` is: the
 * line already says what pressing it does and what it costs, and a button on a
 * line of its own costs a card in a dashboard column the one thing it has least
 * of. Drawn as the text around it with an underline, which is what everything
 * else in this product that leads somewhere looks like - and it inherits the
 * warning colour of the banner it sits in, because it is the answer to the
 * sentence beside it rather than a second thing to notice. */
/* And the one that opens a grid somebody published, which sits in a banner the
   same way and for the same reason: a button drawn as a button, inside a
   sentence, reads as the thing the sentence is about rather than as a word in
   it. */
.finish-read,
.show-published {
  font: inherit;
  border: 0;
  padding: 0;
  background: none;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* While the pages are on their way. The press is gone and the sentence stays,
   so the reader can see that what they asked for is happening rather than
   wondering whether the press landed. */
.finish-read[aria-disabled='true'],
.show-published:disabled {
  cursor: default;
  text-decoration: none;
  opacity: 0.7;
}

/* The word a warning's mark stands for, said to a screen reader and not drawn.
   Clipped rather than display:none, which a screen reader would skip. */
.sign-said {
  position: absolute;
  width: 1px;
  height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* Crosstab Change: what moved since a grid somebody kept or published.

   A list rather than a table, so a dashboard column never runs out of width:
   the name of a cell takes what is left and wraps, and the two figures beside
   it keep their own width. At 346 pixels the longest status name goes onto a
   second line instead of pushing the step off the edge.

   No colour for a direction, for the reason the grid gives its triangles
   none: whether a rise is good news is a fact about the team, and on a field
   of bugs or of issues with no estimate green for a rise says the opposite of
   what is true. The sign says which way; the figure says how far. */
.change-since {
  margin: var(--ds-space-100, 8px) 0 0;
}

.change-most {
  margin: var(--ds-space-150, 12px) 0 0;
  font-weight: 600;
}

.change-moves {
  margin: var(--ds-space-050, 4px) 0 0;
  padding: 0;
  list-style: none;
}

.change-moves li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: var(--ds-space-100, 8px);
  align-items: baseline;
  padding: var(--ds-space-050, 4px) 0;
  border-top: 1px solid var(--ds-border, var(--fallback-border));
  font-variant-numeric: tabular-nums;
}

.change-where {
  overflow-wrap: anywhere;
}

.change-step {
  color: var(--ds-text-subtlest, var(--fallback-text-subtlest));
  white-space: nowrap;
}

.change-of {
  font-weight: 600;
  text-align: end;
  white-space: nowrap;
}

.change-actions,
.change-keep {
  flex-wrap: wrap;
  align-items: center;
  margin-top: var(--ds-space-100, 8px);
}

/* The press that shows every move, on a line of its own under the counts.
   Inline at the end of the sentence it was one unbreakable box, and at the
   width of a dashboard column it ran past the edge of the card. Underlined at
   rest, because it is the one thing in the card that changes what the card
   shows. */
.change-rest .drill {
  display: block;
  margin-top: var(--ds-space-050, 4px);
  padding: 0;
  text-align: start;
  text-decoration: underline;
}

/* The trend through the kept grids, last in the card and folded, so opening
   it moves nothing the reader was reading. The press is underlined at rest
   like the one that shows every move, because it too changes what the card
   shows. */
.change-trend {
  margin-top: var(--ds-space-100, 8px);
}

.change-trend-open {
  padding: 0;
  text-align: start;
  text-decoration: underline;
}

/* The chart gadget's figures, folded under the drawing.

   The table scrolls sideways inside the card rather than widening it: seven
   columns of figures and a row name are wider than a dashboard column, and a
   card that grows past its column pushes the dashboard beside it. Figures on
   one line each and lined up on their digits, like the grid's. */
.plot-figures {
  margin-top: var(--ds-space-100, 8px);
}

.plot-table {
  overflow-x: auto;
}

.plot-table table {
  border-collapse: collapse;
  font-size: max(12px, 0.85em);
  font-variant-numeric: tabular-nums;
}

.plot-table th,
.plot-table td {
  padding: var(--ds-space-025, 2px) var(--ds-space-100, 8px);
  text-align: end;
  white-space: nowrap;
  border-top: 1px solid var(--ds-border, var(--fallback-border));
}

.plot-table th[scope='row'],
.plot-table thead th:first-child {
  text-align: start;
}

/* The second setting, under the first with the space a new subject takes:
 * close beneath publishing's last line, keeping automatically read as more
 * about publishing. */
.admin-setting + .admin-setting {
  margin-block-start: var(--ds-space-200, 16px);
}

/* The site's automatic keeps, under the settings and apart from them by a
 * rule, as the reset is: a list of other people's schedules is a subject of
 * its own, not one more line about the switch above it. */
.admin-schedules {
  margin-block-start: var(--ds-space-300, 24px);
  padding-block-start: var(--ds-space-200, 16px);
  border-block-start: 1px solid var(--ds-border, var(--fallback-border));
}

.admin-schedules h2 {
  margin: 0 0 var(--ds-space-100, 8px);
  font-size: 1rem;
}

.admin-schedule-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.admin-schedule-list li + li {
  margin-block-start: var(--ds-space-150, 12px);
}

.admin-schedule-list p {
  margin: 0 0 var(--ds-space-050, 4px);
}

/* Clearing a development site, under the settings and set apart from them by
 * a rule: the settings are the page's purpose on every site, and this is
 * drawn only on a development one. Without the rule the offer read as one
 * more paragraph about the setting above it. */
.admin-reset {
  margin-block-start: var(--ds-space-300, 24px);
  padding-block-start: var(--ds-space-200, 16px);
  border-block-start: 1px solid var(--ds-border, var(--fallback-border));
}

/* The app's own drop-down: a field that looks like the text boxes beside it,
 * and under it, in the flow, the list it opens.
 *
 * Written against `button.choose[role='combobox']` rather than `.choose`,
 * because the field is a button and sits in rows whose buttons are filled -
 * `.setups button` would draw it as a blue Save, `.keeps button` as a chip -
 * and the field has to read as a field in all of them.
 *
 * The list is the width of whatever the field stands in, which on a dashboard
 * is the card, and its options wrap rather than run past the edge: the long
 * ones are the names of other gadgets and sentences such as `Start from another
 * Crosstab on this dashboard`. Eight rows show before it scrolls inside itself.
 */
button.choose[role='combobox'] {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-100, 8px);
  box-sizing: border-box;
  max-width: 100%;
  min-width: 0;
  min-height: 32px;
  margin: 0;
  font: inherit;
  text-align: start;
  white-space: normal;
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-raised, var(--fallback-surface));
  border: 1px solid var(--ds-border-input, #8590a2);
  border-radius: var(--ds-border-radius, 3px);
  box-shadow: none;
  padding: var(--ds-space-075, 6px) var(--ds-space-100, 8px);
  cursor: pointer;
}

/* The chevron, drawn rather than a glyph so it takes the text colour in both
   themes and is not read out. */
button.choose[role='combobox']::after {
  content: '';
  flex: none;
  width: 6px;
  height: 6px;
  margin-inline-start: auto;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}

button.choose[role='combobox'][aria-expanded='true']::after {
  transform: translateY(2px) rotate(225deg);
}

button.choose[role='combobox']:hover {
  background: var(
    --ds-surface-raised-hovered,
    var(--ds-surface-raised, var(--fallback-surface))
  );
  border-color: var(--ds-border-input, #8590a2);
}

button.choose[role='combobox']:focus-visible {
  outline: 2px solid var(--ds-border-focused, var(--fallback-focus));
  outline-offset: 1px;
}

button.choose[role='combobox'][aria-disabled='true'] {
  color: var(--ds-text-disabled, var(--fallback-disabled));
  background: var(
    --ds-background-disabled,
    var(--ds-surface-raised, var(--fallback-surface))
  );
  cursor: default;
}

/* The rows of small controls under a grid keep their own height. */
.keeps button.choose[role='combobox'],
.series-choice button.choose[role='combobox'] {
  min-height: 0;
  padding: var(--ds-space-050, 4px) var(--ds-space-100, 8px);
}

.choose-shown {
  min-width: 0;
  overflow-wrap: anywhere;
}

.choose-list {
  /* Relative so that its options measure their place from its top, which is
     how the option under the cursor is scrolled into view inside it. Still in
     the flow. */
  position: relative;
  flex-basis: 100%;
  box-sizing: border-box;
  width: 100%;
  max-height: calc(8 * (1.4em + 12px) + 8px);
  margin: var(--ds-space-050, 4px) 0 0;
  padding: var(--ds-space-050, 4px) 0;
  overflow-y: auto;
  list-style: none;
  /* Brought into view clear of the row of Save and Cancel, which stays on
     the bottom of the form. */
  scroll-margin-block-end: 56px;
  color: var(--ds-text, var(--fallback-text));
  background: var(--ds-surface-overlay, var(--fallback-surface));
  border: 1px solid var(--ds-border, var(--fallback-border));
  border-radius: var(--ds-border-radius, 3px);
  box-shadow: var(
    --ds-shadow-overlay,
    0 8px 12px rgba(9, 30, 66, 0.15),
    0 0 1px rgba(9, 30, 66, 0.31)
  );
}

.choose-list[hidden] {
  display: none;
}

.choose-list [role='option'] {
  margin: 0;
  padding: var(--ds-space-075, 6px) var(--ds-space-150, 12px);
  line-height: 1.4;
  overflow-wrap: anywhere;
  cursor: pointer;
}

/* Under the cursor, whether the cursor is the mouse or the arrow keys: the
   field keeps the focus, so this is the only ring the open list has. */
.choose-list [role='option'][aria-selected='true'] {
  background: var(--ds-background-neutral-subtle-hovered, var(--fallback-neutral));
  box-shadow: inset 2px 0 0 var(--ds-border-focused, var(--fallback-focus));
}

/* The one already chosen, which a reader opening the list looks for first. */
.choose-list .chosen {
  font-weight: 600;
  color: var(--ds-text-selected, var(--fallback-selected-text));
}

.choose-list [role='option'][aria-disabled='true'] {
  color: var(--ds-text-disabled, var(--fallback-disabled));
  cursor: default;
}
