/* Console: everything you type into. Fields, toggles, repeatable rows, the
   save bar and the confirm dialog. Values are --cb-* tokens (see console.css
   header for the documented exceptions). */

.cb-form {
  display: grid;
  gap: var(--cb-space-3xl);
}
.cb-fieldset {
  display: grid;
  gap: var(--cb-space-2xl);
  border: 0;
  margin: 0;
  padding: 0;
}
.cb-field {
  display: grid;
  gap: var(--cb-space-md);
}
.cb-field > label {
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-medium);
}
.cb-field-help {
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-faint);
}
.cb-field-error {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-poor-text);
  font-weight: var(--cb-font-weight-medium);
}

.cb-input,
.cb-select,
.cb-textarea {
  width: 100%;
  padding: var(--cb-space-lg) var(--cb-space-xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-surface);
  font-size: var(--cb-type-sm-size);
  transition: border-color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-input:hover,
.cb-select:hover,
.cb-textarea:hover {
  border-color: var(--cbc-accent);
}
.cb-input[aria-invalid="true"] {
  border-color: var(--cbc-poor);
}
.cb-textarea {
  min-height: var(--cb-width-xxs);
  font-family: var(--cb-font-mono);
  resize: vertical;
}
.cb-input[type="number"] {
  font-variant-numeric: tabular-nums;
}
.cb-input:disabled,
.cb-select:disabled {
  background: var(--cbc-sunken);
  color: var(--cbc-ink-faint);
}

/* A row of related controls, e.g. provider + model + budget for one stage. */
.cb-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) repeat(3, minmax(0, 1fr));
  gap: var(--cb-space-xl);
  align-items: end;
  padding: var(--cb-space-xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-2xl);
}
.cb-row + .cb-row {
  margin-top: var(--cb-space-lg);
}
.cb-row-title {
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-semibold);
}
.cb-row-note {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}

/* Repeatable list rows (services, prices, owned domains). */
.cb-list {
  display: grid;
  gap: var(--cb-space-lg);
}
.cb-list-item {
  display: grid;
  gap: var(--cb-space-xl);
  padding: var(--cb-space-2xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-2xl);
  background: var(--cbc-surface);
}
.cb-list-item-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-xl);
}
.cb-list-item-head .cb-row-title {
  margin-inline-end: auto;
}

/* ---- The checkbox ----
   Drawn, not inherited. The browser's own box is the one control on this screen
   nobody chose: it is a different shape on every platform, it does not know the
   dark theme, and beside a switch and a menu that were both built here it reads
   as something that fell out of a 2003 form.

   The input keeps being the input, so focus, keyboard and the label's `for`
   still work exactly as they did; only its appearance is taken over.

   The switch is a checkbox underneath too, and it must not be caught by this:
   source order is not enough, because the tick drawn here is a pseudo-element
   on a :checked state and outweighs the switch's own knob. So the switch says
   what it is, and this rule takes everything else. */
input[type="checkbox"]:not(.cb-switch) {
  appearance: none;
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
  margin: 0;
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-sm);
  background: var(--cbc-surface);
  cursor: pointer;
  transition:
    border-color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cbc-spring);
}
input[type="checkbox"]:not(.cb-switch):hover {
  border-color: var(--cbc-accent);
}
input[type="checkbox"]:not(.cb-switch):checked {
  border-color: var(--cbc-accent);
  background: var(--cbc-accent);
}
/* The tick draws itself in, rather than blinking on with the fill. */
input[type="checkbox"]:not(.cb-switch):checked::after {
  content: "";
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
  background: var(--cbc-on-accent);
  mask: var(--cbc-check) center / contain no-repeat;
  animation: cbc-pop var(--cb-motion-fast) var(--cbc-spring) backwards;
}
input[type="checkbox"]:not(.cb-switch):disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.cb-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-lg);
  font-size: var(--cb-type-sm-size);
  cursor: pointer;
}
.cb-toggle .cb-switch {
  appearance: none;
  inline-size: var(--cb-space-6xl);
  block-size: var(--cb-space-3xl);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-line);
  position: relative;
  cursor: pointer;
  transition: background var(--cb-motion-fast) var(--cbc-spring);
}
.cb-toggle .cb-switch::after {
  content: "";
  position: absolute;
  inset-block-start: var(--cb-space-xs);
  inset-inline-start: var(--cb-space-xs);
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-surface);
  transition: transform var(--cb-motion-fast) var(--cbc-spring);
}
.cb-toggle .cb-switch:checked {
  background: var(--cbc-accent);
}
.cb-toggle .cb-switch:checked::after {
  transform: translateX(var(--cb-space-3xl));
}

/* ---- Connections ----
   One row per credential, inside the panel of the group it belongs to.

   The row is three columns and nothing else: what it is and what it unlocks,
   whether it is live, and what you can do about it. Every row used to carry an
   open password box as a fourth column, so seven of them sat on this screen
   permanently repeating "Paste a new key to replace it" for an action almost
   nobody performs twice a year. The box lives behind Connect now and opens
   across the full width of the row, where a key has room to be read. */
.cb-conns {
  display: grid;
}
.cb-conn {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content max-content;
  align-items: center;
  gap: var(--cb-space-lg) var(--cb-space-2xl);
  padding: var(--cb-space-lg) var(--cb-space-3xl);
}
.cb-conn + .cb-conn {
  border-top: 1px solid var(--cbc-line);
}

.cb-conn-name {
  display: grid;
  gap: var(--cb-space-xxs);
  min-width: 0;
}
.cb-conn-name b {
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-medium);
}
/* Wraps rather than truncates. Every one of these sentences was cut off
   mid-word with an ellipsis, which is a screen showing you that it has
   something to say and then not saying it. */
.cb-conn-name span {
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-sm-line-height);
  max-width: var(--cb-layout-paragraph-max-width);
  color: var(--cbc-ink-faint);
}

/* Fixed columns, not content-sized ones. Each row is its own grid, so a state
   block that sizes to its own text puts every badge at a different distance
   from the left and the column reads as a ragged edge rather than as a column.
   Reserved widths make the badges line up, then the last four, then the date,
   which is the entire point of listing these as a table. */
.cb-conn-state {
  display: grid;
  grid-template-columns: var(--cb-space-10xl) var(--cb-space-8xl) var(--cb-space-10xl);
  align-items: center;
  /* Sized to itself, placed by the column. A grid item stretches to its track
     by default, which forced the status pill to exactly the column width and
     left the word running out past the end of its own background: the chip
     looked cut off on the right because the chip really did stop there. */
  justify-items: start;
  gap: var(--cb-space-lg);
  white-space: nowrap;
}
/* The last four, shown the way a masked key is shown everywhere else. */
.cb-conn-tail {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
}
.cb-conn-when {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
/* The same, for the buttons. Two of these credentials have no live test and
   one has nothing to remove, so a flex row steps the whole action column in
   and out by eighty pixels from row to row. Every row has all three places;
   some of them are empty. */
.cb-conn-actions {
  display: grid;
  grid-template-columns: var(--cb-space-8xl) var(--cb-space-9xl) var(--cb-space-8xl);
  align-items: center;
  justify-items: end;
  gap: var(--cb-space-md);
}

/* ---- Putting a key in ----
   The full width of the row, because a credential is sixty characters long and
   a third of a row is not enough of them to check you pasted the right one. */
.cb-conn-entry {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--cb-space-md);
  padding: var(--cb-space-xl);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-sunken);
  animation: cbc-emerge var(--cb-motion-base) var(--cbc-spring) backwards;
}
.cb-conn-entry-row {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  flex-wrap: wrap;
}
.cb-conn-entry .cb-input {
  flex: 1 1 var(--cb-width-sm);
  min-width: 0;
  padding: var(--cb-space-md) var(--cb-space-lg);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
}
/* Where to get one, written where you are about to paste one rather than
   hidden in a hover on the label three columns away. */
.cb-conn-source {
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-faint);
}

/* The test result sits under the row it belongs to, spanning the full width so
   a provider's error message has room to be read. */
.cb-conn-result {
  grid-column: 1 / -1;
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-faint);
}
.cb-conn-result[data-tone="ok"] {
  color: var(--cbc-good-text);
}
.cb-conn-result[data-tone="bad"] {
  color: var(--cbc-poor-text);
}

/* 1024px mirrors tokens.breakpoint.lg */
@media (max-width: 1024px) {
  .cb-conn {
    grid-template-columns: minmax(0, 1fr) max-content;
  }
  .cb-conn-actions {
    grid-column: 1 / -1;
    justify-content: flex-end;
  }
}

/* ---- Save bar: appears only when something is unsaved ----
   It arrived and left in a single frame, which for the one element that carries
   the whole contract of this surface ("nothing saves silently") is the worst
   thing it could do: a bar that blinks in reads as a repaint, and a bar that
   blinks out reads as something having gone wrong. It comes up from the bottom
   edge and goes back down into it, the same way and with the same two keyframes
   as the board's save sign (.cb-saver in console.css), because it is the same
   idea in a different corner: the edge of the screen talking, not something
   appearing over the work.

   `both` on the entrance rather than `backwards`, which is the exception the
   motion vocabulary allows: cbc-saver-in ends somewhere the keyframe decides
   (see console-theme.css), so it holds. The exit must hold in any case, or the
   bar would snap back to full strength for the frame between the animation
   ending and the element being hidden. */
.cb-savebar {
  position: sticky;
  inset-block-end: var(--cb-space-3xl);
  z-index: var(--cbc-z-savebar);
  display: flex;
  align-items: center;
  gap: var(--cb-space-2xl);
  margin-top: var(--cb-space-4xl);
  padding: var(--cb-space-xl) var(--cb-space-3xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-raised);
  /* Ink, not surface. This read `color: var(--cbc-surface)`, which is the
     colour of a card: dark text on the barely lighter dark bar in the dark
     theme, and white on white in the light one, where the sentence was not
     merely hard to read but absent. The one line this bar exists to say. */
  color: var(--cbc-ink);
  box-shadow: var(--cbc-shadow);
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-medium);
  animation: cbc-saver-in var(--cb-motion-base) var(--cbc-spring) both;
}
.cb-savebar[data-leaving] {
  animation: cbc-saver-out var(--cb-motion-base) var(--cb-motion-ease) both;
}
.cb-savebar-actions {
  margin-inline-start: auto;
  display: flex;
  gap: var(--cb-space-lg);
}

/* ---- Dialog ---- */
/* THE ONE SURFACE THAT ARRIVES FROM NOWHERE, until this. Delete, Rerun and
   Stop this scan all put a veil and a fully formed box on the screen in a
   single frame, which reads as a repaint rather than as an interruption:
   everything else in this console comes from somewhere. cbc-rise and not
   cbc-pop, because a confirm dialog has no anchor to grow out of. Released
   rather than held, so it ends where the element rests (console-theme.css). */
.cb-dialog {
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  width: min(100%, var(--cb-width-md));
  border: 0;
  border-radius: var(--cb-radius-4xl);
  padding: var(--cb-space-5xl);
  background: var(--cbc-surface);
  color: var(--cbc-ink);
  box-shadow: var(--cbc-shadow);
  /* A dialog with more than two fields in it is taller than a short window,
     and a confirm button below the fold is a confirm button nobody can reach.
     The dialog scrolls inside itself rather than the page scrolling under it,
     which is what a modal is for. */
  max-block-size: calc(100svh - var(--cb-space-8xl));
  overflow-y: auto;
}
/* Label and control on one line with a fixed control column, which is the
   density docs/10 asks for. A stack of label-over-control is right on a
   settings screen with room for it and wrong in a 560px dialog. */
.cb-form-rows {
  display: grid;
  gap: var(--cb-space-2xl);
}
/* A block of rows that has nothing in it right now, and must therefore take no
   room: the add-lead dialog nests one for the questions the chosen audit asks,
   and an audit that asks none would otherwise leave a grid gap where a field
   used to be. `display: grid` above beats the browser's own [hidden] rule, so
   the rule has to be written out. */
.cb-form-rows[hidden] {
  display: none;
}
.cb-form-rows .cb-field {
  grid-template-columns: var(--cb-space-11xl) minmax(0, 1fr);
  align-items: baseline;
  gap: var(--cb-space-lg) var(--cb-space-2xl);
}
.cb-form-rows .cb-field > label {
  grid-column: 1;
}
.cb-form-rows .cb-field > :not(label):not(.cb-field-help) {
  grid-column: 2;
}
.cb-form-rows .cb-field-help {
  grid-column: 2;
}
@media (max-width: 640px) {
  /* mirrors tokens.width.lg */
  .cb-form-rows .cb-field {
    grid-template-columns: minmax(0, 1fr);
  }
  .cb-form-rows .cb-field > *,
  .cb-form-rows .cb-field-help {
    grid-column: 1;
  }
}
/* Every modal dialog is dimmed with the console's one veil, whatever is on the
   page underneath it. See --cbc-veil in console-theme.css.

   THE VEIL DOES NOT FADE, AND THAT IS WHAT MADE MODALS FEEL SLOW. It carries a
   backdrop-filter, which draws everything behind it into a texture and filters
   it. Filtering a still page costs one frame and the result is reused; fading
   the element that carries the filter changes the composite on every frame of
   the fade, so a quarter second of animation was a quarter second of
   full-viewport blur. Measured on the card library: five frames over fifty
   milliseconds with the fade, one without, worst frame eighty-seven.

   Nothing is lost by taking it off, because the fade was never the part anybody
   was watching. The panel still arrives on its own animation, out of the middle
   where it belongs, and a dim that is simply there behind a panel that arrives
   is what every modal worth copying does. A veil that fades in under a panel
   that is already moving is two things starting at once, which is also why the
   old one read as slightly soft rather than as crisp.

   The other half of this is that the page underneath stops moving while the
   dialog is up (console-theme.css, "While a modal is up"), so the one filter
   pass this now costs is the only one it costs. */
.cb-dialog::backdrop {
  background: var(--cbc-veil);
  backdrop-filter: var(--cbc-veil-blur);
}
.cb-dialog h2 {
  font-size: var(--cb-type-xl-size);
  line-height: var(--cb-type-xl-line-height);
  font-weight: var(--cb-font-weight-semibold);
  margin-bottom: var(--cb-space-lg);
}
.cb-dialog p {
  font-size: var(--cb-type-sm-size);
  color: var(--cbc-ink-faint);
}
.cb-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--cb-space-lg);
  margin-top: var(--cb-space-4xl);
}

.cb-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-lg);
  align-items: center;
  margin-bottom: var(--cb-space-3xl);
}
.cb-filters .cb-input {
  flex: 1 1 var(--cb-width-xxs);
}
.cb-filters .cb-select {
  flex: 0 0 auto;
  width: auto;
}

/* 768px mirrors tokens.breakpoint.md */
@media (max-width: 768px) {
  .cb-row {
    grid-template-columns: minmax(0, 1fr);
  }
  .cb-savebar {
    flex-wrap: wrap;
    border-radius: var(--cb-radius-3xl);
  }
}

/* The APIs one credential switches on. A single Google Cloud key covers five,
   and the screen used to ask for four separate keys instead, which is how two
   keys from two different projects ended up on file with a different half of
   the APIs enabled on each. */
/* Its own row under the credential, the full width of the card. Inside the
   name cell the fold's space-between summary pushed its two halves to opposite
   ends of a narrow column, which read as two unrelated pieces of text. */
.cb-conn-powers {
  grid-column: 1 / -1;
  margin-block-start: var(--cb-space-xs);
  padding-block-start: var(--cb-space-lg);
  border-block-start: 1px solid var(--cbc-line);
}
/* Sized to its own words and pushed left, rather than stretched across the
   card by the fold's default. */
.cb-conn-powers > summary {
  display: inline-flex;
  gap: var(--cb-space-md);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
.cb-conn-powers > summary .cb-fold-more {
  margin-inline-start: 0;
}
.cb-conn-powers .cb-table {
  margin-block-start: var(--cb-space-lg);
}
.cb-conn-powers .cb-table td {
  vertical-align: top;
}
.cb-conn-powers .cb-field-help {
  margin-block-start: var(--cb-space-md);
}
.cb-conn-api-list {
  display: grid;
  gap: var(--cb-space-sm);
  margin-block-start: var(--cb-space-md);
  padding-inline-start: 0;
  list-style: none;
}
.cb-conn-api-list li {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--cb-space-md);
  font-size: var(--cb-type-xs-size);
}
.cb-conn-api-list b {
  font-weight: var(--cb-font-weight-medium);
}
.cb-conn-api-list span {
  color: var(--cbc-ink-muted);
}

/* ---- Asking somebody why ----
   THE ACTION ROW HAD NO RULE AT ALL, which is the whole of why this dialog
   looked unfinished: `.cb-dialog-acts` was written in reason-dialog.ts and
   never styled, so Cancel and the primary sat flush under the box, hard left,
   at the same weight, with the finishing button faded because it is off until
   something is typed. It reads now the way every other pair of decisions in
   this console reads: apart from what they act on, at the far end, in the order
   the eye leaves the box in.

   The hairline is what makes the difference between a form with buttons under
   it and a form with a foot. */
.cb-dialog-acts {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--cb-space-lg);
  margin-block-start: var(--cb-space-4xl);
  padding-block-start: var(--cb-space-2xl);
  border-block-start: 1px solid var(--cbc-line);
  /* STAYS REACHABLE IN A DIALOG THAT SCROLLS.
     The dialog caps at the window and scrolls inside itself, which is right:
     the alternative is a confirm button off the bottom of the screen. But it
     left the confirm at the end of a scroll with nothing saying it was there,
     and a dialog that looks like it has no way to agree is one people press
     Escape on. Sticky costs nothing on a short dialog, where there is no
     scrolling for it to stick against.

     The ground is painted because the content scrolls UNDER this: transparent,
     the rows slide through the buttons. The negative margin and matching
     padding put the paint out to the dialog's own edges, which the padding on
     .cb-dialog would otherwise inset it from. */
  position: sticky;
  inset-block-end: calc(var(--cb-space-5xl) * -1);
  margin-inline: calc(var(--cb-space-5xl) * -1);
  padding-inline: var(--cb-space-5xl);
  padding-block-end: var(--cb-space-5xl);
  background: var(--cbc-surface);
}

/* The heading, and the mark from the control that opened this beside it. */
.cb-why-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--cb-space-xl);
}
.cb-why-head h2 {
  margin-block-end: var(--cb-space-sm);
}
.cb-why-mark {
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-6xl);
  block-size: var(--cb-space-6xl);
  border-radius: var(--cb-radius-2xl);
  background: var(--cbc-accent-wash);
  color: var(--cbc-accent);
}
.cb-why-mark .cb-icon {
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
}

/* The box, and how much room is left in it. The count sits inside the field's
   own corner rather than under it, so it costs no height on the ninety-nine
   per cent of openings where it says nothing at all. */
.cb-why-box {
  position: relative;
  display: grid;
}
.cb-why-box .cb-input {
  padding-block-end: var(--cb-space-4xl);
}
.cb-why-left {
  position: absolute;
  inset-inline-end: var(--cb-space-lg);
  inset-block-end: var(--cb-space-lg);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  pointer-events: none;
}

/* ONE ENTRANCE, STAGGERED, rather than a scatter of little ones (rule 14). The
   dialog rises, and the rungs of the ladder arrive behind it in the order they
   mean something in. Twenty-five milliseconds a rung, which is the beat the
   onboarding cards already use for a list somebody is about to read down. */
.cb-why .cb-ask-seat {
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: calc(var(--i, 0) * var(--cb-motion-step));
}
@media (prefers-reduced-motion: reduce) {
  .cb-why .cb-ask-seat {
    animation: none;
  }
}

/* The stack of fields inside a dialog. It had no rule at all, so every field
   sat flush against the help text of the one above it and a label read as the
   last line of the previous paragraph. Two dialogs use this class. */
.cb-dialog-form {
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-3xl);
  /* And the same distance from whatever the dialog said above it, so the first
     label does not read as the last line of the standfirst. */
  margin-block-start: var(--cb-space-3xl);
}

/* ---- Bringing an estate in from a spreadsheet ----
   Three steps in one dialog: choose a file, map the columns, look at what will
   happen. The last one draws a table, which is why it wears the wide dialog. */

/* The file step. A dropped-file target that is honest about being a button:
   the input is the control, and everything around it is the explanation. */
.cb-drop {
  display: grid;
  justify-items: center;
  gap: var(--cb-space-lg);
  padding: var(--cb-space-6xl) var(--cb-space-3xl);
  border: 1px dashed var(--cbc-line-strong);
  border-radius: var(--cb-radius-2xl);
  background: var(--cbc-sunken);
  text-align: center;
}
.cb-drop .cb-icon {
  color: var(--cbc-ink-faint);
}
.cb-drop-lead {
  font-size: var(--cb-type-md-size);
  font-weight: var(--cb-font-weight-semibold);
}
/* The note reads as a paragraph rather than as a caption under a heading, so
   it keeps a measure: centred text at full dialog width is unreadable. */
.cb-drop .cb-set-note {
  max-inline-size: var(--cb-width-xs);
}

/* The mapping step: the console's own settings rows, in a dialog. Reused
   deliberately, because "a label, a sentence under it, and one control on the
   right" is a shape this product already has and somebody already knows. */
.cb-sets {
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-xs);
}
/* Its own width rather than `.cb-dialog-wide` as well, because two rules
   setting the same property on one element is decided by load order and this
   file's wide rule comes later. One class, one width. Wider than that one
   because this dialog holds a settings row four times over and then a table of
   four columns: at 640 the label column is 230px and every note wrapped to five
   lines, so the step read as a wall of text beside four menus. */
.cb-dialog-import {
  width: min(100%, var(--cb-width-xl));
}

/* ---- Putting a backup back ---- */
/* Wider than an ordinary dialog because the thing being read in it is a table
   of counts, and a count that wraps onto a second line stops being a count you
   can compare against the one above it. */
.cb-dialog-wide {
  width: min(100%, var(--cb-width-lg));
}

.cb-restore-plan {
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-md);
}
/* The numbers themselves. Sunken rather than raised: this is the part of the
   dialog being read, not a control, and lifting it would make it compete with
   the button underneath that acts on it. */
.cb-restore-list {
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-sm);
  padding: var(--cb-space-lg) var(--cb-space-xl);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-sunken);
}
.cb-restore-row {
  display: flex;
  justify-content: space-between;
  gap: var(--cb-space-xl);
  font-size: var(--cb-type-sm-size);
}
.cb-restore-name {
  font-weight: var(--cb-font-weight-medium);
}
.cb-restore-counts {
  color: var(--cbc-ink-muted);
  text-align: end;
  font-variant-numeric: tabular-nums;
}
/* Which parts go back, when somebody is choosing. Two columns, because the
   names are short and a single column of fifteen checkboxes is a scroll inside
   a dialog. */
.cb-restore-parts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--cb-width-xxs), 1fr));
  gap: var(--cb-space-sm) var(--cb-space-xl);
}
.cb-restore-part {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  font-size: var(--cb-type-sm-size);
}
/* A warning inside the plan reads as a warning without becoming an error: this
   is something to weigh, not something that has gone wrong. */
.cb-field-help[data-tone="warn"] {
  color: var(--cbc-warn-text);
}

/* Prose, not a prompt. The plain textarea is set in the mono face and stands a
   fixed height because everywhere else it holds a prompt or a JSON body; the
   message a prospect reads when the form is off is a sentence somebody writes,
   and it should look like one. */
.cb-textarea-short {
  min-height: 0;
  font-family: var(--cb-font-body);
}

/* ---- The security check's own parts ----
   The QR square, the key in letters, the six digit box, the ten recovery codes
   and the line that says they have been kept. Everything a code screen has that
   no other form in the console does.

   They sit here rather than beside either screen that wears them, because both
   do. The gate between signing in and the console draws them on the setup
   questions' surface (admin/src/mfa, console-gate.css), and the profile screen
   draws the same objects in a dialog when somebody asks for a code or for a
   fresh set of recovery codes (views/code-dialog.ts). One set of names for one
   set of objects, so the two screens cannot quietly drift apart. */

/* 208, composed the way --cbc-rail is, because the scale has 160 and 320 and
   neither is right. At 320 the button on this step sat below the fold on a
   laptop in a video call, which is exactly the window somebody sets this up
   in; at 160 the modules of a dense otpauth URI get small enough that phone
   cameras start hunting for it. */
.cb-qr {
  display: block;
  width: calc(var(--cb-space-11xl) + var(--cb-space-6xl));
  height: calc(var(--cb-space-11xl) + var(--cb-space-6xl));
}

/* The key in letters, for anybody who cannot use the square. */
.cb-manual > summary {
  cursor: pointer;
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
  text-align: center;
}
.cb-manual-key {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  justify-content: center;
}
.cb-manual-code {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  background: var(--cbc-sunken);
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border-radius: var(--cb-radius-md);
  word-break: break-all;
}

/* The code box: big, centred, and spaced out, because it is read off a phone
   and typed under mild time pressure. */
.cb-code-row {
  display: grid;
  gap: var(--cb-space-sm);
  justify-items: center;
}
.cb-code-label {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
}
.cb-code-input {
  text-align: center;
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-lg-size);
  letter-spacing: var(--cb-tracking-md);
  max-width: var(--cb-width-xxs);
}

/* The recovery codes, in two columns so ten of them read as a set rather than
   as a long list somebody scrolls past. */
.cb-codes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--cb-space-sm) var(--cb-space-xl);
  list-style: none;
  margin: 0;
  padding: var(--cb-space-xl);
  background: var(--cbc-sunken);
  border-radius: var(--cb-radius-xl);
}
.cb-recovery-code {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-sm-size);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.cb-keep {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  justify-content: center;
  font-size: var(--cb-type-sm-size);
  cursor: pointer;
}
