/*
  Controls, forms, tables and the small pieces of furniture.

  Buttons and fields keep the Bootstrap class names the templates already use, so that every screen
  is restyled at once rather than half of them being redrawn and half left behind. The names are a
  vocabulary; nothing here is Bootstrap.
*/

@layer components {
  .backlink {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    margin-bottom: var(--s-3);
    padding: var(--s-1) var(--s-2);
    margin-inline-start: calc(var(--s-2) * -1);
    border-radius: var(--radius);
    font-size: var(--text-sm);
    color: var(--ink-2);
    text-decoration: none;
  }

  .backlink:hover {
    color: var(--ink);
    background: var(--surface-sunk);
    text-decoration: none;
  }

  .backlink .icon { color: var(--ink-3); }
  .backlink:hover .icon { color: inherit; }

  /* Records ---------------------------------------------------------------- */

  /* A record reads as a ruled list: the field name on the left, the value on the right. */
  .datum-row {
    display: grid;
    grid-template-columns: minmax(9rem, 16ch) 1fr;
    gap: var(--s-1) var(--s-4);
    align-items: baseline;
    padding-block: var(--s-3);
    border-bottom: 1px solid var(--rule);
  }

  .datum-row:last-child { border-bottom: 0; }

  .datum-row .label { display: inline-flex; align-items: baseline; gap: var(--s-2); }
  .datum-row .label .icon { align-self: center; color: var(--ink-3); }

  /* One vertical rhythm ----------------------------------------------------- */

  /* The reset takes every margin off, so blocks that follow one another — a heading, a sentence,
     a row of buttons — are spaced by the container rather than by the elements themselves. A
     container that stacks its children carries .stack and they are separated by one step of the
     spacing scale, which is why no screen sets a margin of its own. */
  .stack > * + * { margin-block-start: var(--stack, var(--s-3)); }
  .stack--tight { --stack: var(--s-2); }
  .stack--loose { --stack: var(--s-5); }

  .section-heading {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--ink-2);
    margin-block: var(--s-5) var(--s-2);
    padding-bottom: var(--s-1);
    border-bottom: 1px solid var(--rule-strong);
  }

  .blank-slate {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-3);
    padding: var(--s-5) var(--s-4);
    border: 1px dashed var(--rule-strong);
    border-radius: var(--radius-lg);
    color: var(--ink-2);
    margin-block: var(--s-3) var(--s-5);
  }

  .blank-slate p { margin: 0; }
  .blank-slate .icon { color: var(--ink-3); }

  /* Something is happening in the background. */
  .icon--spin { animation: icon-spin 1.2s linear infinite; }

  @keyframes icon-spin { to { transform: rotate(360deg); } }

  @media (prefers-reduced-motion: reduce) {
    .icon--spin { animation: none; }
  }

  /* Two things side by side when there is room, one above the other when there is not. */
  .split {
    display: grid;
    gap: var(--s-5);
    align-items: start;
    margin-block: var(--s-4) var(--s-5);
  }

  @media (min-width: 48em) {
    .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  }

  .split > section > .section-heading:first-child { margin-top: 0; }

  /* A short account: what is owed, line by line, totalled under a rule. */
  .ledger { margin: 0; }

  .ledger__row {
    display: flex;
    justify-content: space-between;
    gap: var(--s-4);
    padding-block: var(--s-2);
    border-bottom: 1px solid var(--rule);
  }

  .ledger__row dt, .ledger__row dd { margin: 0; }
  .ledger__row dt { color: var(--ink-2); min-width: 0; }
  .ledger__row dt small { display: block; font-size: var(--text-sm); color: var(--ink-3); }
  .ledger__row dd { font-variant-numeric: tabular-nums; white-space: nowrap; }
  .ledger__row--total { border-bottom: 0; border-top: 2px solid var(--ink); margin-top: var(--s-1); padding-top: var(--s-3); }
  .ledger__row--total dt, .ledger__row--total dd { font-weight: 700; color: var(--ink); font-size: var(--text-lg); }
  .ledger__row--quiet dd { color: var(--ink-2); }
  .ledger__row--credit dd { color: var(--money); }

  /* Choosing a customer for a payer: the search box takes the room, the button sits beside it. */
  .match-form { display: flex; gap: var(--s-2); align-items: center; }
  .match-form .selectize-control, .match-form select { flex: 1; min-width: 12rem; }
  .match-form .selectize-control.form-control { border: 0; padding: 0; height: auto; background: none; }

  /* Buttons side by side, wrapping when the row is narrow. */
  .button-row { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }

  .table .actions { white-space: nowrap; }
  .table .actions form { display: inline-block; }

  /* Who a GoCardless payer is, one line per thing we know about them. */
  .payer { margin: 0; line-height: var(--leading); }
  .payer strong { display: block; }
  .payer span { display: block; font-size: var(--text-sm); color: var(--ink-2); }
  .likely { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-2); }

  /* Guides ------------------------------------------------------------------- */

  /* A guide reads as one column of prose with a picture after each step. */
  .guide { max-width: 44rem; }
  .guide h1 { margin-bottom: var(--s-2); }
  .guide__lead { font-size: var(--text-lg); color: var(--ink-2); margin-bottom: var(--s-5); }
  .guide h2 { font-size: var(--text-lg); margin: var(--s-7) 0 var(--s-3); padding-top: var(--s-4); border-top: 1px solid var(--rule); }
  .guide p { max-width: var(--measure); }

  .guide__contents { margin-bottom: var(--s-6); }
  .guide__contents ol { margin: 0; padding-left: 1.25rem; columns: 2; column-gap: var(--s-5); }
  .guide__contents li { padding-block: 2px; break-inside: avoid; }

  .guide__figure { margin: var(--s-4) 0 var(--s-5); }
  .guide__figure img {
    display: block;
    width: 100%;
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sheet);
  }
  .guide__figure figcaption { margin-top: var(--s-2); font-size: var(--text-sm); color: var(--ink-2); }

  @media (max-width: 36em) {
    .guide__contents ol { columns: 1; }
  }

  /* Setting up -------------------------------------------------------------- */

  /* The steps to get something working, read top to bottom as a path. Each step is a ruled row: a
     mark on the left says whether it is done, the middle says what it is and where it stands, and
     anything still to do sits on the right. A step that is itself a sequence carries numbered
     sub-steps. Marks take the accent when done because done is what the eye is looking for. */
  .setup {
    list-style: none;
    margin: var(--s-4) 0 0;
    padding: 0;
    max-width: 52rem;
  }

  .setup__step {
    position: relative;
    display: grid;
    grid-template-columns: var(--tap) 1fr;
    gap: var(--s-2) var(--s-3);
    padding-block: var(--s-4);
    border-bottom: 1px solid var(--rule);
  }

  .setup__step:last-child { border-bottom: 0; }

  /* The path between marks. */
  .setup__step:not(:last-child)::before {
    content: "";
    position: absolute;
    inset-inline-start: calc(var(--tap) / 2 - 1px);
    top: calc(var(--s-4) + var(--tap) * 0.75);
    bottom: calc(var(--tap) / -4);
    border-inline-start: 2px solid var(--rule);
  }

  .setup__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap);
    height: var(--tap);
    border-radius: 50%;
    border: 2px solid var(--rule-strong);
    background: var(--surface);
    color: var(--ink-3);
    font-family: var(--face-display);
    font-weight: 700;
  }

  .setup__step--done .setup__mark { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
  .setup__step--failed .setup__mark { background: var(--owing-soft); border-color: var(--owing); color: var(--owing); }
  .setup__step--done:not(:last-child)::before { border-color: var(--accent); }

  .setup__title {
    margin: 0;
    font-size: var(--text-lg);
    line-height: var(--tap);
  }

  .setup__body { min-width: 0; }
  .setup__body p { color: var(--ink-2); max-width: var(--measure); }
  .setup__body .datum { display: inline; color: var(--ink); }
  .setup__action { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }
  .setup__action .form-inline { gap: var(--s-2); }
  details.setup__action { display: block; }
  details.setup__action summary { display: inline-block; list-style: none; cursor: pointer; }
  details.setup__action summary::-webkit-details-marker { display: none; }
  details.setup__action[open] summary { margin-bottom: var(--s-2); }

  /* A step that is two things done in order. */
  .setup__substeps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--s-4);
  }

  .setup__substep {
    display: grid;
    grid-template-columns: 1.5rem 1fr;
    gap: var(--s-3);
    align-items: start;
  }

  .setup__numeral {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    border: 1.5px solid var(--rule-strong);
    font-family: var(--face-display);
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--ink-2);
    line-height: 1;
  }

  .setup__substep--done .setup__numeral { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
  .setup__substep h4 { margin: 0; font-size: var(--text); line-height: 1.5rem; }

  /* A setting that is one checkbox. */
  .setting {
    display: grid;
    grid-template-columns: var(--tap) 1fr;
    gap: var(--s-3);
    align-items: start;
    max-width: 52rem;
    padding-block: var(--s-4);
  }

  .setting--quiet { padding-top: 0; }
  .setting__box { display: flex; justify-content: center; padding-top: 0.2rem; }
  .setting__box input { width: 1.25rem; height: 1.25rem; accent-color: var(--accent); cursor: pointer; }
  .setting label { margin: 0; font-size: var(--text-lg); font-family: var(--face-display); font-weight: 700; cursor: pointer; }
  .setting p { margin: var(--s-1) 0 0; color: var(--ink-2); max-width: var(--measure); }

  /* A list of places to go, each with its own mark. */
  .cardlist { list-style: none; padding: 0; margin: 0; max-width: 40rem; }

  .cardlist__item {
    display: flex;
    align-items: flex-start;
    gap: var(--s-3);
    padding: var(--s-4) var(--s-2);
    border-bottom: 1px solid var(--rule);
    text-decoration: none;
    color: var(--ink);
  }

  .cardlist__item:hover { background: var(--surface-sunk); }
  .cardlist__item .icon { margin-top: 2px; color: var(--ink-3); }
  .cardlist__title { display: block; font-weight: 700; }
  .cardlist__note { display: block; font-size: var(--text-sm); color: var(--ink-2); }

  /* Buttons ---------------------------------------------------------------- */

  .btn {
    --btn-face: var(--surface);
    --btn-ink: var(--ink);
    --btn-rule: var(--rule-strong);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    min-height: var(--tap);
    padding: var(--s-2) var(--s-4);
    border: 1px solid var(--btn-rule);
    border-radius: var(--radius);
    background: var(--btn-face);
    color: var(--btn-ink);
    font-weight: 700;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.12s ease, border-color 0.12s ease;
  }

  .btn:hover { background: color-mix(in oklab, var(--btn-face) 92%, var(--ink)); }

  .btn:disabled, .btn.disabled { opacity: 0.5; cursor: not-allowed; }

  .btn-sm { min-height: 2rem; padding: var(--s-1) var(--s-3); font-size: var(--text-sm); }
  .btn-lg { min-height: 2.75rem; padding: var(--s-3) var(--s-5); font-size: var(--text-lg); }
  .btn-block { display: flex; width: 100%; }

  /* The one filled button on a screen is the thing to do next. */
  .btn-primary {
    --btn-face: var(--accent);
    --btn-ink: var(--accent-ink);
    --btn-rule: var(--accent);
  }

  .btn-success {
    --btn-face: var(--money);
    --btn-ink: var(--money-ink);
    --btn-rule: var(--money);
  }

  .btn-danger {
    --btn-face: var(--owing);
    --btn-ink: var(--owing-ink);
    --btn-rule: var(--owing);
  }

  .btn-secondary, .btn-dark {
    --btn-face: var(--surface-sunk);
    --btn-ink: var(--ink);
  }

  .btn-light, .btn-outline-secondary {
    --btn-face: var(--surface);
    --btn-ink: var(--ink-2);
    --btn-rule: var(--rule);
  }

  .btn-outline-primary { --btn-face: var(--surface); --btn-ink: var(--accent); --btn-rule: var(--accent); }
  .btn-outline-success { --btn-face: var(--surface); --btn-ink: var(--money); --btn-rule: var(--money); }
  .btn-outline-danger  { --btn-face: var(--surface); --btn-ink: var(--owing); --btn-rule: var(--owing); }

  .btn-link {
    --btn-face: transparent;
    --btn-rule: transparent;
    --btn-ink: var(--ink);
    text-decoration: underline;
    text-underline-offset: 0.2em;
    font-weight: 400;
  }

  .btn-group {
    display: inline-flex;
  }

  /* A split button's menu is the group's last child, so the rounding has to count buttons only. */
  .btn-group > .btn { border-radius: 0; margin-inline-start: -1px; }

  .btn-group > .btn:nth-child(1 of .btn) {
    border-start-start-radius: var(--radius);
    border-end-start-radius: var(--radius);
    margin-inline-start: 0;
  }

  .btn-group > .btn:nth-last-child(1 of .btn) {
    border-start-end-radius: var(--radius);
    border-end-end-radius: var(--radius);
  }

  /* Forms ------------------------------------------------------------------ */

  .form-group { margin-bottom: var(--s-4); }

  label, .col-form-label {
    display: inline-block;
    font-weight: 700;
    font-size: var(--text-sm);
    margin-bottom: var(--s-1);
  }

  .col-form-label { padding-top: calc(var(--s-2) + 1px); margin-bottom: 0; }

  legend { font-weight: 700; font-size: var(--text-sm); padding: 0; }

  .form-control, .form-control-file, .custom-select, .selectize-input {
    display: block;
    width: 100%;
    min-height: var(--tap);
    padding: var(--s-2) var(--s-3);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
  }

  textarea.form-control { min-height: 5rem; resize: vertical; }

  select.form-control, .custom-select {
    appearance: none;
    padding-inline-end: var(--s-6);
    background-image: linear-gradient(45deg, transparent 50%, currentcolor 50%),
                      linear-gradient(135deg, currentcolor 50%, transparent 50%);
    background-position: right 1.05rem center, right 0.8rem center;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
  }

  .form-control:focus, .custom-select:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 1px;
  }

  .form-control.is-invalid { border-color: var(--owing); }

  .invalid-feedback { color: var(--owing); font-size: var(--text-sm); margin-top: var(--s-1); }

  .form-text { display: block; font-size: var(--text-sm); color: var(--ink-2); margin-top: var(--s-1); }

  .text-muted { color: var(--ink-2); }

  .form-check, .custom-control {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--s-2);
    min-height: var(--tap);
  }

  .form-check-label, .custom-control-label { cursor: pointer; }
  .form-check-input, .custom-control-input { cursor: pointer; }

  .form-check-inline, .custom-control-inline { display: inline-flex; margin-inline-end: var(--s-4); }

  .form-check-input, .custom-control-input {
    width: 1.15rem;
    height: 1.15rem;
    margin: 0;
    accent-color: var(--money);
    flex: none;
  }

  .form-check-label, .custom-control-label {
    margin-bottom: 0;
    font-weight: 400;
    font-size: var(--text);
  }

  .input-group { display: flex; }
  .input-group > .form-control { border-radius: 0; }
  .input-group > :first-child, .input-group > :first-child .input-group-text { border-start-start-radius: var(--radius); border-end-start-radius: var(--radius); }
  .input-group > :last-child, .input-group > :last-child .input-group-text { border-start-end-radius: var(--radius); border-end-end-radius: var(--radius); }

  .input-group-text {
    display: flex;
    align-items: center;
    padding: var(--s-2) var(--s-3);
    border: 1px solid var(--rule-strong);
    background: var(--surface-sunk);
    color: var(--ink-2);
    white-space: nowrap;
  }

  .input-group-prepend > .input-group-text { border-inline-end: 0; }
  .input-group-append  > .input-group-text { border-inline-start: 0; }

  /* A form's buttons sit together with air above them, not jammed under the last field. */
  .form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin-top: var(--s-5);
    padding-top: var(--s-4);
    border-top: 1px solid var(--rule);
  }

  .prose-form { max-width: 34rem; }

  /* Numbered because these really are steps, taken in order. */
  .steps { list-style: none; counter-reset: step; padding: 0; margin: 0 0 var(--s-6); }

  .steps__step {
    counter-increment: step;
    position: relative;
    padding-inline-start: var(--s-6);
    padding-block: var(--s-3);
    border-top: 1px solid var(--rule);
  }

  .steps__step::before {
    content: counter(step);
    position: absolute;
    inset-inline-start: 0;
    top: var(--s-3);
    width: 1.5rem;
    height: 1.5rem;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 700;
  }

  .steps__title { font-size: var(--text); margin-bottom: var(--s-1); }

  /* A row of filters. Each control keeps room for its longest option and the row wraps rather
     than squeezing one of them until its text is cut off. */
  .filterbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    align-items: center;
    margin-bottom: var(--s-5);
  }

  .filterbar__field { flex: 1 1 12rem; min-width: 12rem; }
  .filterbar__field--wide { flex: 2 1 18rem; }
  .filterbar__field .form-group { margin-bottom: 0; }
  .filterbar__actions { flex: 0 0 auto; }

  @media (max-width: 35.99em) {
    .filterbar__field, .filterbar__field--wide { flex-basis: 100%; min-width: 0; }
    .filterbar__actions, .filterbar__actions .btn { width: 100%; }
  }

  /* Tables ----------------------------------------------------------------- */

  /* A wide table scrolls inside its own box rather than pushing the page sideways. */
  .table-responsive, .table-sm-responsive { overflow-x: auto; }

  .table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
  }

  .table th, .table td {
    padding: var(--s-3);
    text-align: start;
    vertical-align: top;
    border-bottom: 1px solid var(--rule);
  }

  .table thead th {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--ink-2);
    border-bottom: 1px solid var(--rule-strong);
    white-space: nowrap;
  }

  .table-sm th, .table-sm td { padding: var(--s-2) var(--s-3); }

  .table tbody tr:hover { background: var(--surface-sunk); }

  /* Rules, not boxes: striping and outer borders only add noise to a ruled list. */
  .table-striped tbody tr:nth-of-type(odd) { background: none; }
  .table-bordered th, .table-bordered td { border-inline: 0; }
  .table-clear td { border: 0; }

  /* Sort headings carry the direction as a caret, and the sorted one is set in ink. */
  a.sort_link { color: var(--ink-2); text-decoration: none; white-space: nowrap; }
  a.sort_link:hover { color: var(--ink); }
  a.sort_link.asc, a.sort_link.desc { color: var(--ink); }

  /* Never set this on the cell itself: a table cell told to be a flex container leaves the table
     layout, and the row's rules stop short of the column edge. */
  .cell-with-mark {
    display: flex;
    align-items: baseline;
    gap: var(--s-2);
  }

  .cell-with-mark > .icon,
  .cell-with-mark > .roundtag__dot { align-self: center; color: var(--ink-3); }

  /* Row controls sit together at the end of a row and never wrap onto two lines. */
  .row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    justify-content: flex-end;
    white-space: nowrap;
  }

  .section-heading--flush { margin-block: 0 0; border-bottom: 0; }

  /* A settings section title with its mark: the icon takes the quiet ink so the words lead. */
  .h3--iconed { display: flex; align-items: center; gap: var(--s-2); }
  .h3--iconed .icon { flex-shrink: 0; color: var(--ink-3); }

  /* Cards ------------------------------------------------------------------ */

  .card {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
  }

  .card + .card { margin-top: var(--s-3); }
  .card-body { padding: var(--s-4); }
  .card-header { padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--rule); }
  .card-footer { padding: var(--s-3) var(--s-4); border-top: 1px solid var(--rule); }
  .card-title { font-size: var(--text-sm); font-weight: 700; color: var(--ink-2); margin-bottom: var(--s-2); }
  .card-text { margin: 0; }

  /* As received ------------------------------------------------------------ */

  /* A message as the customer sees it. The field is the phone's screen, edged with a border that
     grades from the accent to its tint. A text the company sent is the accent bubble a sent message
     is on any phone; an email is a sheet with its envelope lines above the words. */
  .as-received {
    margin: 0;
    padding: var(--s-5);
    border: 1.5px solid transparent;
    border-radius: var(--radius-lg);
    background:
      linear-gradient(var(--paper), var(--paper)) padding-box,
      linear-gradient(160deg, var(--accent), var(--accent-soft) 55%, var(--rule)) border-box;
    max-width: 36rem;
  }

  .as-received--compact { padding: var(--s-4); max-width: 24rem; margin-top: var(--s-3); }

  .as-received__sender {
    font-family: var(--face-display);
    font-weight: 700;
    font-size: var(--text-sm);
    color: var(--ink-2);
    text-align: center;
    margin-bottom: var(--s-2);
  }

  .as-received .as-received__bubble {
    position: relative;
    margin: 0 0 0 auto;
    width: fit-content;
    max-width: min(100%, 30rem);
    padding: var(--s-3) var(--s-4);
    background: var(--accent);
    color: var(--accent-ink);
    border-radius: var(--radius-lg) var(--radius-lg) var(--radius) var(--radius-lg);
    line-height: var(--leading);
    overflow-wrap: anywhere;
    white-space: pre-wrap;
    box-shadow: var(--shadow-sheet);
  }

  .as-received__bubble:empty::before { content: "\00a0"; }

  .as-received__note {
    margin-top: var(--s-2);
    text-align: end;
    font-size: var(--text-xs);
    color: var(--ink-2);
  }

  .as-received__note--owing { color: var(--owing); font-weight: 700; }

  .as-received__sheet {
    background: var(--surface);
    border-radius: var(--radius);
    padding: var(--s-4) var(--s-5) var(--s-5);
    box-shadow: var(--shadow-sheet);
  }

  .as-received__envelope {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--s-1) var(--s-3);
    margin: 0 0 var(--s-3);
    padding-bottom: var(--s-3);
    border-bottom: 1px solid var(--rule);
    font-size: var(--text-sm);
  }

  .as-received__envelope dt { color: var(--ink-3); }
  .as-received__envelope dd { margin: 0; color: var(--ink-2); overflow-wrap: anywhere; }
  .as-received__subject { margin: 0 0 var(--s-3); font-size: var(--text-lg); }
  .as-received__body p { margin: 0 0 var(--s-3); max-width: var(--measure); }
  .as-received__body p:last-child { margin-bottom: 0; }

  /* Toasts ----------------------------------------------------------------- */

  /* A word of confirmation that floats in where the eye is and leaves on its own. Green because it
     is good news that needs no reading: the check says it. */
  .toasts {
    position: fixed;
    inset-inline: 0;
    bottom: var(--s-6);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s-2);
    pointer-events: none;
    z-index: 1050;
  }

  .toast {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-4);
    border-radius: 999px;
    background: var(--money);
    color: var(--money-ink);
    font-weight: 700;
    box-shadow: var(--shadow-sheet);
    pointer-events: auto;
    cursor: pointer;
    animation: toast-in 200ms ease-out;
  }

  .toast--problem { background: var(--owing); color: var(--owing-ink); }
  .toast--leaving { opacity: 0; transition: opacity 200ms ease-in; }

  @keyframes toast-in {
    from { opacity: 0; transform: translateY(var(--s-3)); }
    to { opacity: 1; transform: none; }
  }

  @media (prefers-reduced-motion: reduce) {
    .toast { animation: none; }
    .toast--leaving { transition: none; }
  }

  /* Badges ----------------------------------------------------------------- */

  .badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25em;
    padding: 0.15em 0.5em;
    border-radius: var(--radius);
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: 1.4;
    background: var(--surface-sunk);
    color: var(--ink-2);
    vertical-align: baseline;
  }

  .badge-success { background: var(--money-soft); color: var(--money); }
  .badge-danger  { background: var(--owing-soft); color: var(--owing); }
  .badge-warning { background: var(--owing-soft); color: var(--owing); }
  /* A customer's status is not money, so it takes the accent rather than the money colour. */
  .badge-info, .badge-primary { background: var(--accent-soft); color: var(--accent); }
  .badge-secondary, .badge-light, .badge-dark { background: var(--surface-sunk); color: var(--ink-2); }

  /* Alerts ----------------------------------------------------------------- */

  .alert {
    padding: var(--s-3) var(--s-4);
    border: 1px solid var(--rule);
    border-inline-start: 3px solid var(--accent);
    border-radius: var(--radius);
    background: var(--surface-sunk);
    margin-bottom: var(--s-4);
  }

  .alert-success { border-inline-start-color: var(--money); background: var(--money-soft); color: var(--money); }
  .alert-danger, .alert-error { border-inline-start-color: var(--owing); background: var(--owing-soft); color: var(--owing); }
  .alert-warning { border-inline-start-color: var(--owing); background: var(--owing-soft); color: var(--owing); }
  .alert a { color: inherit; }

  /* The flash sits above the page card on desktop, so it takes the card's spacing rather than
     sitting tight under the top bar. */
  @media (min-width: 48em) {
    #flash_messages .alert {
      margin-top: var(--s-5);
      padding: var(--s-4) var(--s-6);
    }
  }

  .alert-dismissible { display: flex; align-items: flex-start; gap: var(--s-3); }
  .alert-dismissible .close {
    margin-inline-start: auto;
    background: none;
    border: 0;
    font-size: var(--text-lg);
    line-height: 1;
    cursor: pointer;
    color: inherit;
  }

  /* Tabs ------------------------------------------------------------------- */

  .nav-tabs {
    display: flex;
    gap: var(--s-1);
    list-style: none;
    margin: 0 0 var(--s-5);
    padding: 0;
    border-bottom: 1px solid var(--rule);
    overflow-x: auto;
  }

  .nav-tabs .nav-link {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-3);
    text-decoration: none;
    color: var(--ink-2);
    white-space: nowrap;
  }

  .nav-tabs .nav-link:hover { color: var(--ink); }

  .nav-tabs .nav-link.active {
    color: var(--accent);
    font-weight: 700;
    box-shadow: inset 0 -2px 0 var(--accent);
  }

  /* Dropdowns -------------------------------------------------------------- */
  /* Bootstrap's JavaScript toggles .show, so that is the contract these honour. */

  .dropdown, .btn-group { position: relative; }

  .dropdown-menu {
    display: none;
    position: absolute;
    inset-inline-start: 0;
    top: 100%;
    z-index: 40;
    min-width: 12rem;
    padding: var(--s-1);
    margin-top: var(--s-1);
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sheet);
  }

  .dropdown-menu.show { display: block; }
  .dropdown-menu-right { inset-inline-start: auto; inset-inline-end: 0; }

  .dropdown-item {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    width: 100%;
    padding: var(--s-2) var(--s-3);
    border: 0;
    border-radius: var(--radius);
    background: none;
    text-align: start;
    text-decoration: none;
    color: var(--ink);
    cursor: pointer;
  }

  .dropdown-item:hover { background: var(--surface-sunk); }

  .dropdown-divider { height: 1px; margin: var(--s-1) 0; background: var(--rule); }

  .dropdown-heading {
    padding: var(--s-1) var(--s-3);
    font-size: var(--text-xs);
    color: var(--ink-3);
  }

  .dropdown-item[aria-current="true"] { font-weight: 700; }
  .dropdown-item[aria-current="true"]::after { content: "\2713"; margin-inline-start: auto; }

  .dropdown-toggle::after {
    content: "";
    display: inline-block;
    margin-inline-start: var(--s-2);
    border: 4px solid transparent;
    border-top-color: currentcolor;
    translate: 0 2px;
  }

  /* Collapse --------------------------------------------------------------- */

  .collapse:not(.show) { display: none; }
  .collapsing { overflow: hidden; height: 0; transition: height 0.2s ease; }

  /* Modals ----------------------------------------------------------------- */

  .modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1050;
    overflow-y: auto;
    padding: var(--s-4);
  }

  .modal-dialog {
    width: min(100%, 34rem);
    margin-inline: auto;
    margin-block: var(--s-6) var(--s-7);
  }

  .modal-lg .modal-dialog, .modal-dialog.modal-lg { width: min(100%, 52rem); }

  .modal-content {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sheet);
    overflow: hidden;
  }

  .modal-header {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-4);
    border-bottom: 1px solid var(--rule);
  }

  .modal-title { font-size: var(--text-lg); font-weight: 700; margin: 0; }

  .modal-header .close {
    margin-inline-start: auto;
    background: none;
    border: 0;
    font-size: var(--text-xl);
    line-height: 1;
    cursor: pointer;
    color: var(--ink-2);
  }

  .modal-body { padding: var(--s-4); }

  .modal-footer {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    justify-content: flex-end;
    padding: var(--s-4);
    border-top: 1px solid var(--rule);
  }

  .modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1040;
    /* Near-black in both themes: at 45% the difference between the two was not visible. */
    background: #0c110f;
    opacity: 0;
  }

  .modal-backdrop.show { opacity: 0.45; }

  body.modal-open { overflow: hidden; }

  .fade { transition: opacity 0.15s linear; }
  .fade:not(.show) { opacity: 0; }
  .modal.fade .modal-dialog { transition: translate 0.2s ease; translate: 0 -0.75rem; }
  .modal.show .modal-dialog { translate: 0 0; }

  /* Pagination ------------------------------------------------------------- */

  .pagination {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1);
    list-style: none;
    padding: 0;
    margin: var(--s-5) 0 0;
  }

  .page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--tap);
    min-height: var(--tap);
    padding-inline: var(--s-2);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--ink-2);
  }

  .page-link:hover { background: var(--surface-sunk); color: var(--ink); }

  .page-item.active .page-link {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
    font-weight: 700;
  }

  .page-item.disabled .page-link { opacity: 0.4; pointer-events: none; }

  /* What a customer sees after paying or setting up a Direct Debit ---------- */

  .receipt {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sheet);
    padding: var(--s-6) var(--s-5) var(--s-5);
  }

  .receipt__mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--s-7);
    height: var(--s-7);
    margin: 0 0 var(--s-4);
    border-radius: 50%;
    background: var(--money-soft);
    color: var(--money);
  }

  /* Nothing happened, so the mark is quiet rather than a green tick. */
  .receipt__mark--quiet { background: var(--surface-sunk); color: var(--ink-3); }

  .receipt__title { margin: 0; }

  .receipt__who {
    margin: var(--s-1) 0 0;
    font-family: var(--face-display);
    font-size: var(--text-lg);
    line-height: var(--leading-tight);
    color: var(--ink-2);
  }

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

  .receipt__facts li {
    padding-block: var(--s-3);
    border-bottom: 1px solid var(--rule);
    color: var(--ink-2);
  }

  .receipt__facts li:last-child { border-bottom: 0; }
  .receipt__facts .money { color: var(--money); font-weight: 700; }

  .receipt__contact-lead { margin: var(--s-3) 0 var(--s-2); color: var(--ink-2); }

  .receipt__contact {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
  }

  .receipt__contact li {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    min-height: var(--tap);
  }

  .receipt__contact .icon { color: var(--ink-3); flex: none; }

  .receipt__aside {
    margin: var(--s-5) 0 0;
    padding-top: var(--s-3);
    border-top: 1px solid var(--rule);
    font-size: var(--text-sm);
    color: var(--ink-3);
  }
}
