/* What the screens are made of. The palette, type and form come from tokens.css; this file is
   the arrangement.

   Logical properties throughout - inline-start, not left - so the sheet is translated rather
   than rewritten if it ever needs to be.

   Hand-written and small on purpose. A component library is a decision nobody has taken, and
   vendoring one now would make taking it a removal job. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  color-scheme: light dark;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* Every figure on every screen. Tabular so two columns of money align character for character,
   which is the entire reason a person opens this tool. */
.figure,
.mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* The structural device: a press mark naming what follows. */
.label {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}

.page {
  max-inline-size: var(--page);
  margin-inline: auto;
  padding-inline: var(--gap-wide);
  padding-block-end: var(--gap-section);
}

/* A screen that is nothing but the sign-in card fills the viewport by itself, so the wrapper's
   bottom padding - which is there to keep the last docket off the edge - would put a scrollbar
   on a page that fits exactly. Written as a rule about what the page contains rather than as a
   subtraction of the padding's value, so it cannot drift when that value changes. */
.page:has(> .gate:not(.gate--inline)) {
  max-inline-size: none;
  padding: 0;
}

/* --- masthead -------------------------------------------------------------
   The wordmark states the direction of the flow, because that is the one thing about this tool
   somebody has to hold in their head: PrintSmith is the source, Xero is the destination. */

.masthead {
  border-block-end: 1px solid var(--rule);
  background: var(--paper);
}

.masthead__inner {
  max-inline-size: var(--page);
  margin-inline: auto;
  padding: var(--gap) var(--gap-wide);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap-wide);
}

/* The screen's own control - a lookup field, a date range - is what gives way when the row is
   too narrow, and it takes a row of its own rather than pushing the identity onto one. Who is
   signed in is constant chrome on every screen; the control belongs to one screen, and a
   search bar spanning the row under the nav reads as deliberate in a way a displaced name
   does not. */
.masthead__tool {
  flex: 1 1 auto;
  display: flex;
  justify-content: flex-end;
  min-inline-size: 0;
  order: 1;
}

.masthead__tool:not(:empty) {
  flex-basis: 100%;
}

@media (min-width: 1500px) {
  /* Wide enough for everything on one row, so the control goes back beside the nav. */
  .masthead__tool:not(:empty) {
    flex-basis: auto;
    order: 0;
  }
}

.wordmark {
  font-family: var(--mono);
  font-size: var(--t-title);
  font-weight: 600;
  letter-spacing: 0.04em;
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 0.4em;
}

.wordmark__psv {
  color: var(--plate-psv);
}

.wordmark__xero {
  color: var(--plate-xero);
}

.wordmark__arrow {
  color: var(--ink-faint);
  font-size: 0.8em;
}

/* --- the lookup -----------------------------------------------------------
   One field and one action. The action is named for what it does. */

.lookup {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: var(--gap-tight);
  margin-inline-start: auto;
}

.lookup__field {
  font-family: var(--mono);
  font-size: var(--t-figure);
  padding: 0.5rem 0.7rem;

  /* Wants 14rem and gives it up rather than pushing the page sideways. A fixed width here put
     a horizontal scrollbar on every screen with a lookup, at every phone width. */
  flex: 1 1 14rem;
  min-inline-size: 0;
  color: var(--ink);
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius-control);
}

/* The centre beside the number, sized to its own content.

   Both fields asking for 14rem is two thirds of the bar for a name and a number, which pushed the
   button onto a line of its own. The number is the part somebody types and varies; the centre is
   picked from a list and wants only as much room as the longest name in it. */
.lookup__field--centre {
  flex: 0 1 auto;
  min-inline-size: 8rem;
  max-inline-size: 14rem;
}

.lookup__field::placeholder {
  color: var(--ink-faint);
}

/* Inline-block, not inline. The class is worn by a <button>, which is inline-block already, and
   by an <a> that leaves for Xero - which is not: an inline element's vertical padding overflows
   its line box instead of pushing anything away, so the button sat on top of the sentence above
   it on the one screen somebody outside this business ever sees. */
.button {
  display: inline-block;
  font-family: var(--sans);
  font-size: var(--t-body);
  font-weight: 600;
  padding: 0.5rem 1.1rem;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: var(--radius-control);
  cursor: pointer;
}

.button:hover {
  background: var(--plate-psv);
  border-color: var(--plate-psv);
}

/* A button that cannot be pressed has to look like one.

   There was no rule for this, so a disabled button was indistinguishable from a live one:
   pressing it did nothing and explained nothing, which reads as the application being broken.
   The hover response is removed too, because hover is what says "this will do something". */
.button:disabled,
.button:disabled:hover {
  color: var(--ink-faint);
  background: var(--paper);
  border-color: var(--rule-soft);
  cursor: not-allowed;
}

.button--quiet:disabled,
.button--quiet:disabled:hover {
  color: var(--ink-faint);
  background: transparent;
  border-color: var(--rule-soft);
}

:focus-visible {
  outline: 2px solid var(--plate-psv);
  outline-offset: 2px;
}

/* --- the verdict ----------------------------------------------------------
   The hero, because under pressure the first thing somebody needs is the answer, not the data.
   The rule down its inline-start edge carries the only status colour on the page. */

.verdict {
  margin-block-start: var(--gap-section);
  padding: var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-inline-start: 4px solid var(--registered);
  border-radius: var(--radius);
}

.verdict--unsettled {
  border-inline-start-color: var(--plate-xero);
}

.verdict__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap);
}

.verdict__state {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--registered);
  margin: 0;
}

.verdict--unsettled .verdict__state {
  color: var(--plate-xero);
}

.verdict__invoice {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}

.verdict__summary {
  font-size: var(--t-hero);
  line-height: 1.22;
  font-weight: 600;
  letter-spacing: -0.015em;
  max-inline-size: var(--measure);
  margin: var(--gap) 0 0;
}

.verdict__remedy {
  color: var(--ink-dim);
  max-inline-size: var(--measure);
  margin: var(--gap) 0 0;
}

/* --- the registration strip -----------------------------------------------
   The signature, and the reason this tool looks like what it does.

   Each row holds the same field from both systems. When the two agree they sit on one baseline
   in one ink: IN REGISTER. When they disagree they split into their own plates and are pushed
   apart by --misregister, which is exactly what a misregistered print looks like on press.

   The misalignment IS the information. It is not decoration, and it is not the only signal -
   colour, the mark, and a stated difference all say the same thing, so it survives being read
   by somebody who cannot see the offset or the hue. */

.strip {
  margin-block-start: var(--gap-section);
}

.strip__rows {
  margin-block-start: var(--gap);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--paper);
}

.row {
  display: grid;
  grid-template-columns: 9rem 1fr auto 1fr 11rem;
  align-items: center;
  gap: var(--gap);
  padding: 0.7rem var(--gap-wide);
  border-block-end: 1px solid var(--rule-soft);
}

.row:last-child {
  border-block-end: none;
}

.row__field {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.row__value {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-figure);
}

.row__value--psv {
  text-align: end;
}

.row__value--xero {
  text-align: start;
}

/* In register: one ink, one baseline. */
.row--registered .row__value {
  color: var(--ink);
}

/* Out of register: each plate takes its own ink and its own baseline. */
.row--out .row__value--psv {
  color: var(--plate-psv);
  background: var(--plate-psv-wash);
  translate: 0 calc(var(--misregister) * -1);
  padding: 0.1rem 0.4rem;
}

.row--out .row__value--xero {
  color: var(--plate-xero);
  background: var(--plate-xero-wash);
  translate: 0 var(--misregister);
  padding: 0.1rem 0.4rem;
}

.row__note {
  font-family: var(--mono);
  font-size: var(--t-fine);
  text-align: end;
  color: var(--registered);
}

.row--out .row__note {
  color: var(--plate-xero);
}

/* The registration mark itself: the target a press operator lines plates up against. Whole when
   the values agree, split into two offset halves when they do not. */
.mark {
  inline-size: 22px;
  block-size: 22px;
  display: block;
  overflow: visible;
  /* The two marks sit on top of one another and are pulled apart by the offset below, so they
     overlap the way two plates do rather than sitting side by side. */
  isolation: isolate;
}

.mark__psv,
.mark__xero {
  /* Both drawn at the same origin; only the offset separates them. */
  transform-box: fill-box;
}

.mark__ring,
.mark__cross {
  stroke: var(--registered);
  stroke-width: 1.25;
  fill: none;
}

/* Pulled apart on both axes. A press misregisters in whatever direction the plate has shifted,
   and a purely vertical split reads as a design choice rather than as a fault. */
.row--out .mark__psv {
  translate: calc(var(--misregister) * -0.7) calc(var(--misregister) * -1);
}

.row--out .mark__xero {
  translate: calc(var(--misregister) * 0.7) var(--misregister);
}

/* The stroke is set on the PATH, not on the group. A group selector loses to .mark__cross on the
   path itself, and the mark would draw in the agreement colour while sitting in the split
   position - saying two different things at once. */
.row--out .mark__psv .mark__cross {
  stroke: var(--plate-psv);
}

.row--out .mark__xero .mark__cross {
  stroke: var(--plate-xero);
}

/* --- the plates -----------------------------------------------------------
   The detail behind the verdict, in the order somebody reads it: the source, the destination,
   and what we would send. Secondary to the verdict on purpose - most questions are answered
   before a reader gets here. */

.plates {
  margin-block-start: var(--gap-section);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: var(--gap);
  align-items: start;
}

.plate {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--gap-wide);
}

.plate__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap-tight);
  padding-block-end: var(--gap-tight);
  border-block-end: 1px solid var(--rule);
}

.plate--psv .plate__name {
  color: var(--plate-psv);
}

.plate--xero .plate__name {
  color: var(--plate-xero);
}

.plate__name {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  margin: 0;
}

.plate__status {
  font-family: var(--mono);
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

.field {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap);
  padding-block: 0.35rem;
  border-block-end: 1px solid var(--rule-soft);
}

.field:last-child {
  border-block-end: none;
}

.field__name {
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

.field__value {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-fine);
  text-align: end;
}

.lines {
  margin-block-start: var(--gap);
  padding-block-start: var(--gap-tight);
  border-block-start: 1px solid var(--rule);
}

.line {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--gap);
  padding-block: 0.3rem;
  font-size: var(--t-fine);
}

.line__description {
  white-space: pre-line;
  color: var(--ink-dim);
}

.line__amount {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

.line--money .line__description,
.line--money .line__amount {
  color: var(--ink);
  font-weight: 600;
}

.absent {
  color: var(--ink-faint);
  font-style: italic;
  margin: var(--gap) 0 0;
}

/* --- nothing looked up yet ------------------------------------------------
   An invitation to act, not an apology for being empty. */

.opening {
  margin-block-start: var(--gap-section);
  padding: var(--gap-section) var(--gap-wide);
  background: var(--paper);
  border: 1px dashed var(--rule);
  border-radius: var(--radius);
  text-align: center;
}

.opening__line {
  font-size: var(--t-title);
  max-inline-size: 46ch;
  margin-inline: auto;
  margin-block: var(--gap) 0;
}

.opening__hint {
  color: var(--ink-dim);
  font-size: var(--t-fine);
  margin-block-start: var(--gap-tight);
}

.problem {
  margin-block-start: var(--gap-wide);
  padding: var(--gap);
  background: var(--plate-xero-wash);
  border-inline-start: 3px solid var(--plate-xero);
  border-radius: var(--radius);
}

/* --- the plates settling into register ------------------------------------
   One moment, on load, and it carries meaning rather than attention: the rows that AGREE slide
   into alignment, and the rows that do not stay where they are. A press being brought into
   register, and a reader watching which rows refuse to. */

@media (prefers-reduced-motion: no-preference) {
  .row--registered .row__value {
    animation: settle 420ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
  }

  .row--registered .row__value--psv {
    --from: calc(var(--misregister) * -1);
  }

  .row--registered .row__value--xero {
    --from: var(--misregister);
  }

  @keyframes settle {
    from {
      translate: 0 var(--from, 0);
      opacity: 0.55;
    }

    to {
      translate: 0 0;
      opacity: 1;
    }
  }
}

@media (max-width: 40rem) {
  .row {
    grid-template-columns: 1fr auto 1fr;
    grid-template-areas:
      "field field field"
      "psv mark xero"
      "note note note";
    row-gap: var(--gap-tight);
  }

  .row__field {
    grid-area: field;
  }

  .row__note {
    grid-area: note;
    text-align: start;
  }

  .lookup {
    inline-size: 100%;
    margin-inline-start: 0;
  }

  .lookup__field {
    inline-size: auto;
    flex: 1;
  }
}

/* --- nav ------------------------------------------------------------------
   Two screens, and which one you are on. Set in the label face rather than the body face so it
   reads as apparatus and never competes with the answer below it. */

.nav {
  display: flex;

  /* Wraps, because seven screens do not fit across a phone and the alternative is a page that
     scrolls sideways. Somebody answering a franchisee on a mobile is exactly who this tool is
     for, and a sideways scrollbar is how they conclude it is not for them. */
  flex-wrap: wrap;
  gap: var(--gap-tight) var(--gap);
}

.nav__link {
  font-family: var(--sans);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-faint);
  padding-block: 0.35rem;
  border-block-end: 2px solid transparent;
}

.nav__link:hover {
  color: var(--ink);
}

.nav__link--here {
  color: var(--ink);
  border-block-end-color: var(--plate-psv);
}

.wordmark {
  text-decoration: none;
}

/* --- the date range -------------------------------------------------------
   Two fields rather than one, because a range typed as text is a range typed wrongly. The labels
   stay visible: "from" and "to" on two identical boxes is not something to make somebody guess. */

.lookup--range {
  align-items: center;
  gap: var(--gap-tight);
}

.lookup--range .label {
  margin: 0;
}

.lookup__field--date {
  inline-size: auto;
  font-size: var(--t-fine);
}

/* --- the tally ------------------------------------------------------------
   What the run did, in one line, before anything it found. The money is set as a figure because
   it is the number somebody reads out on the phone. */

.tally {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap);
  padding-block: var(--gap-wide);
  border-block-end: 1px solid var(--rule);
}

.tally__line {
  font-size: var(--t-title);
  margin: 0;
}

/* The sentence under the count. Quieter than the count and wider than a label, because it
   explains how the whole list behaves rather than reporting a number. */
.tally__note {
  margin: var(--gap-tight) 0 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

.tally__stake {
  display: flex;
  align-items: baseline;
  gap: var(--gap-tight);
  margin: 0;
}

/* --- an unfinished range --------------------------------------------------
   Above the findings, never below. A run that could not read part of its range has found nothing
   about that part, and somebody reading the groups first has already formed a view by the time a
   footnote reaches them. Carries the out-of-register colour because that is what it is. */

.unfinished {
  margin-block-start: var(--gap-wide);
  padding: var(--gap) var(--gap-wide);
  background: var(--plate-xero-wash);
  border-inline-start: 3px solid var(--plate-xero);
  border-radius: var(--radius);
}

.unfinished p {
  margin-block: var(--gap-hair) 0;
}

.unfinished__list {
  list-style: none;
  margin-block: var(--gap) 0;
  padding: 0;
  display: grid;
  gap: var(--gap-tight);
}

.unfinished__list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap-tight);
}

.unfinished__reason {
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

/* --- the findings ---------------------------------------------------------
   One block per fault, most blocking first. Each is a docket: what it is, how many, what it is
   worth, what is wrong, what to do, and then the invoices themselves. */

.findings {
  display: grid;
  gap: var(--gap-wide);
  margin-block-start: var(--gap-wide);
}

.finding {
  padding-block-end: var(--gap-wide);
  border-block-end: 1px solid var(--rule-soft);
}

.finding__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap-tight) var(--gap);
  margin-block-end: var(--gap-tight);
}

.finding__name {
  font-size: var(--t-title);
  font-weight: 600;
  margin: 0;
}

.finding__count {
  font-family: var(--mono);
  font-size: var(--t-fine);
  color: var(--ink-dim);
  margin: 0;
}

.finding__stake {
  margin: 0;
  margin-inline-start: auto;
}

.finding__summary {
  margin: 0;
  color: var(--ink);
  max-inline-size: var(--measure);
}

.finding__remedy {
  margin-block: var(--gap-hair) 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: 62ch;
}

.finding__invoices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-tight);
  margin-block-start: var(--gap);
}

/* --- an invoice chip ------------------------------------------------------
   The invoice number and what it puts at stake, as one target. Every one opens the invoice
   screen, which is the whole point of grouping: the queue says what to do, the X-ray says why. */

.chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--gap-tight);
  padding: 0.3rem 0.6rem;
  text-decoration: none;
  color: var(--ink);
  background: var(--paper-sunk);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius-control);
}

.chip:hover {
  border-color: var(--plate-psv);
  background: var(--plate-psv-wash);
}

.chip__invoice {
  font-size: var(--t-fine);
}

.chip__amount {
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

.chip--unread {
  font-size: var(--t-fine);
  padding-block: 0.15rem;
}

/* --- the export -----------------------------------------------------------
   Quiet, because it is the last thing somebody does rather than the first. */

.export {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap);
  margin-block-start: var(--gap-section);
  padding-block-start: var(--gap-wide);
  border-block-start: 1px solid var(--rule);
}

.button--quiet {
  color: var(--ink);
  background: transparent;
  border-color: var(--rule);
  text-decoration: none;
}

.button--quiet:hover {
  color: var(--paper);
  background: var(--plate-psv);
  border-color: var(--plate-psv);
}

.export__note {
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

/* --- the centres -----------------------------------------------------------
   A register of what exists, read the way a register is: name first, then the facts that decide
   whether you need to open it. Which system holds the Xero token is on the row rather than
   inside, because during a cutover it is the only thing anybody is scanning for. */

.centres {
  display: grid;
  gap: 1px;
  background: var(--rule-soft);
  border-block: 1px solid var(--rule-soft);
}

.centre {
  display: grid;
  grid-template-columns: minmax(10rem, 1fr) auto auto auto;
  align-items: baseline;
  gap: var(--gap);
  padding: var(--gap) var(--gap-tight);
  background: var(--paper);
  text-decoration: none;
  color: var(--ink);
}

.centre:hover {
  background: var(--plate-psv-wash);
}

.centre__name {
  font-size: var(--t-figure);
  font-weight: 600;
}

.centre__zone {
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

/* Paused is the resting state and reads quietly; syncing carries the one status colour, because
   a centre that is live is the one worth noticing in a list of ninety. */
.centre__state {
  font-family: var(--sans);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.centre__state--on {
  color: var(--registered);
}

.centre__owner {
  font-size: var(--t-fine);
  color: var(--ink-dim);
  text-align: end;
}

/* max-width, not max-inline-size: the latter is a container-query feature and a
   media query naming it is silently dropped. This block did nothing until it was
   spelled the way a media query is spelled. */
@media (max-width: 40rem) {
  .centre {
    grid-template-columns: 1fr auto;
  }

  .centre__owner {
    grid-column: 1 / -1;
    text-align: start;
  }
}


/* --- a form that changes something -----------------------------------------
   Set apart from the lookups above it. Those read; these write, and a person should be able to
   tell which they are looking at without reading the button. */

.register {
  margin-block-start: var(--gap-section);
  padding: var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

/* A form that has to be in the DOM before it has anything to show. Static rendering matches a
   posted form name against what the page rendered on the request that carries it, so a form
   that appears only once it is useful cannot receive its own post. The frame can go, though. */
.register--bare {
  padding: 0;
  background: none;
  border: 0;
}

.register__title {
  font-size: var(--t-title);
  font-weight: 600;
  margin: 0 0 var(--gap-tight);
}

.register__form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--gap);
  margin-block-start: var(--gap);
}

.register__field {
  display: grid;
  gap: var(--gap-hair);
}

.register__field .label {
  margin: 0;
}

.register__note {
  margin-block: var(--gap) 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: 62ch;
}

.inline-form {
  margin-block-start: var(--gap);
}

/* A centre's own page reuses the finding blocks, so each concern reads as one thing to deal
   with rather than as a settings sheet. */
.finding .register__form {
  margin-block-start: var(--gap);
}

/* A pasted access token is long and arrives as one unbroken run of characters. It gets a field
   that can show more than a fragment of it, so somebody can see they pasted the whole thing. */

.register__field--wide {
  flex-basis: 100%;
}

.lookup__field--long {
  inline-size: 100%;
  font-size: var(--t-fine);
  line-height: 1.4;
  resize: vertical;
  word-break: break-all;
}

/* --- a caveat ---------------------------------------------------------------
   An answer with an edge to it, beside the verdict rather than below the panels: a limit read
   after somebody has formed a view has arrived too late to change it. Quieter than the
   out-of-register colour, because the answer above it still stands. */

.caveat {
  margin-block-start: var(--gap);
  padding: var(--gap) var(--gap-wide);
  background: var(--paper-sunk);
  border-inline-start: 3px solid var(--ink-faint);
  border-radius: var(--radius);
  max-inline-size: 78ch;
}

.caveat p {
  margin-block: var(--gap-hair) 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

/* --- the doctor ------------------------------------------------------------
   A print shop runs a job off a docket: the number, the stock, and the things that have to be
   signed off before the press turns. That is exactly what a centre's configuration is, so a
   centre reads as one, and the sign-offs that are already done fold out of the way.

   The colour lives on the inline-start edge and in one small press mark, and nowhere else. A page
   of ninety centres has to be scannable down a single column, and a screen that shouts everywhere
   says nothing. */

.dockets {
  display: grid;
  gap: var(--gap-wide);
  margin-block-start: var(--gap-wide);
}

.docket {
  padding: var(--gap) var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-inline-start: 3px solid var(--rule);
  border-radius: var(--radius);
}

.docket--ready { border-inline-start-color: var(--registered); }
.docket--fix { border-inline-start-color: var(--plate-xero); }
.docket--unknown { border-inline-start-color: var(--ink-faint); }
.docket--note { border-inline-start-color: var(--plate-psv); }

.docket__head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--gap-hair) var(--gap);
  align-items: baseline;
}

.docket__name {
  font-size: var(--t-title);
  font-weight: 600;
  margin: 0;
}

.docket__name a {
  color: inherit;
  text-decoration: none;
}

.docket__name a:hover {
  text-decoration: underline;
}

.docket__summary {
  grid-column: 1 / -1;
  margin: 0;
  margin-block-end: var(--gap-tight);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* The one place a centre's state is named in words. Set as a press mark so it reads as a stamp
   on the docket rather than as another sentence competing with the summary. */
.docket__state {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
  text-align: end;
}

/* Coloured by what it says, not by what the docket says.

   These two are different questions: the docket's mark is "does this centre need anybody",
   which a single open fault is enough to make true, and this word is "is it set up". A centre
   that is set up and has one fault would otherwise read "set up" in the ink that means
   broken. */
.docket__state--ready { color: var(--registered); }
.docket__state--fix { color: var(--plate-xero); }
.docket__state--note { color: var(--plate-psv); }

/* --- one check ------------------------------------------------------------
   Two columns at the top - what was checked, and how it came out - then the finding and the
   remedy running the full width, because both are prose somebody reads rather than scans. */

.check {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--gap-hair) var(--gap);
  align-items: baseline;
  padding-block: var(--gap-tight);
  border-block-start: 1px solid var(--rule-soft);
}

.check__what {
  font-size: var(--t-fine);
  font-weight: 600;
  margin: 0;
}

.check__state {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
  text-align: end;
}

.check--fix > .check__state { color: var(--plate-xero); }
.check--note > .check__state { color: var(--plate-psv); }
.check--ready > .check__state { color: var(--registered); }

.check__finding,
.check__remedy {
  grid-column: 1 / -1;
  font-size: var(--t-fine);
  margin: 0;
  max-inline-size: var(--measure);
}

.check__finding {
  color: var(--ink-dim);
}

/* The remedy is the half somebody acts on, so it is set as an instruction rather than as more
   description of the fault - full ink, and marked with the turn from finding to doing. */
.check__remedy {
  color: var(--ink);
  padding-inline-start: 1.1em;
  text-indent: -1.1em;
  margin-block-start: var(--gap-hair);
}

.check__remedy::before {
  content: "→";
  color: var(--ink-faint);
  margin-inline-end: 0.5em;
}

/* --- what is already right ------------------------------------------------
   Folded, not dropped. Most complaints are answered by proving nothing is wrong, and that
   argument needs the list of what was actually looked at. */

.docket__settled {
  margin-block-start: var(--gap-tight);
}

.docket__settled > summary {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  cursor: pointer;
  padding-block: var(--gap-tight);
  border-block-start: 1px solid var(--rule-soft);
}

/* The browser's own marker sits hard against a tracked uppercase label, so it is replaced with
   one that has room to breathe and turns when the list opens. */
.docket__settled > summary::marker {
  content: "";
}

.docket__settled > summary::before {
  content: "+";
  display: inline-block;
  inline-size: 1.2em;
  color: var(--ink-faint);
}

.docket__settled[open] > summary::before {
  content: "\2212";
}

.docket__settled > summary:hover {
  color: var(--ink-dim);
}

.docket__settled[open] > summary {
  color: var(--ink-dim);
}

/* --- what has run ----------------------------------------------------------
   A press log: one line per job, what it was, and whether it came off the press. Scanned down
   the left for the type and down the right for the marks, so a failed run is findable without
   reading a word of prose. */

.entries {
  display: grid;
  gap: var(--gap-tight);
  margin-block-start: var(--gap-wide);
}

.entry {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--gap-hair) var(--gap);
  align-items: baseline;
  padding: var(--gap) var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-inline-start: 3px solid var(--rule);
  border-radius: var(--radius);
  color: inherit;
  text-decoration: none;
}

.entry:hover {
  border-color: var(--rule);
  border-inline-start-color: var(--plate-psv);
}

.entry--done { border-inline-start-color: var(--registered); }
.entry--broke { border-inline-start-color: var(--plate-xero); }
.entry--going { border-inline-start-color: var(--plate-psv); }

.entry__what {
  font-size: var(--t-title);
  font-weight: 600;
  margin: 0;
}

.entry__state {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
  text-align: end;
}

.entry--done .entry__state { color: var(--registered); }
.entry--broke .entry__state { color: var(--plate-xero); }

.entry__where {
  grid-column: 1 / -1;
  font-size: var(--t-fine);
  color: var(--ink-faint);
  margin: 0;
}

.entry__summary {
  grid-column: 1;
  margin: var(--gap-hair) 0 0;
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

.entry__slugs {
  margin: var(--gap-hair) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-tight);
  justify-content: flex-end;
}

/* One small fact attached to a row, set the way a slug is set on a proof: a short label naming
   something about the piece it sits beside. Never the only carrier of a status - the edge rule
   and the worded state say the same thing - so a reader who cannot tell the inks apart loses
   nothing.

   Not .mark, which is already the registration mark on the invoice screen. Two things named
   alike in one stylesheet is one of them quietly overriding the other. */
.slug {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  color: var(--ink-faint);
  white-space: nowrap;
}

/* The one mark that has to be unmissable: whether this was allowed to change anything outside
   this system. */
.slug--live {
  color: var(--plate-xero);
  border-color: var(--plate-xero);
}

.slug--read {
  color: var(--registered);
  border-color: var(--registered);
}

.slug--quiet {
  border-color: var(--rule-soft);
}

/* --- one operation --------------------------------------------------------- */

.verdict--pending {
  border-inline-start-color: var(--plate-psv);
}

.verdict--pending .verdict__state {
  color: var(--plate-psv);
}

.verdict__slugs {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-tight);
}

.verdict__where {
  margin: var(--gap-tight) 0 0;
  font-size: var(--t-fine);
  color: var(--ink-faint);
  max-inline-size: var(--measure);
}

.steps,
.calls {
  margin-block-start: var(--gap-section);
}

.steps__none {
  color: var(--ink-dim);
  max-inline-size: var(--measure);
  margin-block-start: var(--gap-tight);
}

/* Numbered, because the order is the content: what it decided, and why the next thing
   followed. */
.step {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: var(--gap-hair) var(--gap);
  padding-block: var(--gap-tight);
  border-block-start: 1px solid var(--rule-soft);
}

.step__ordinal {
  color: var(--ink-faint);
  font-size: var(--t-fine);
  margin: 0;
}

.step__name {
  font-size: var(--t-fine);
  font-weight: 600;
  margin: 0;
}

.step__note {
  grid-column: 2;
  margin: 0;
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* --- one call --------------------------------------------------------------
   Folded by default. A reconciliation makes hundreds, and the summary line carries everything
   needed to find the one that matters. */

.call {
  border-block-start: 1px solid var(--rule-soft);
}

.call__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap-tight) var(--gap);
  padding-block: var(--gap-tight);
  cursor: pointer;
  font-size: var(--t-fine);
}

.call__system {
  font-weight: 600;
  min-inline-size: 6rem;
}

.call__method {
  color: var(--ink-faint);
}

/* Drawn here because a flex summary has no marker of its own. */
.call__open {
  color: var(--ink-faint);
  font-size: var(--t-fine);
  transition: rotate 120ms ease;
}

.call[open] .call__open {
  rotate: 90deg;
}

/* Which endpoint, so a run of three hundred calls is a list somebody can scan. Truncated
   rather than wrapped: the whole URL is one line below, once the row is open. */
.call__where {
  color: var(--ink-dim);
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.call__outcome {
  color: var(--registered);
  font-weight: 600;
}

.call--refused .call__outcome {
  color: var(--plate-xero);
}

.call__took,
.call__under {
  color: var(--ink-faint);
  margin-inline-start: auto;
}

.call__url {
  font-size: var(--t-fine);
  color: var(--ink-dim);
  overflow-wrap: anywhere;
  margin: 0 0 var(--gap-tight);
}

.call__failure {
  margin: 0 0 var(--gap-tight);
  padding: var(--gap-tight);
  background: var(--plate-xero-wash);
  border-inline-start: 3px solid var(--plate-xero);
  border-radius: var(--radius);
  font-size: var(--t-fine);
}

.call__note {
  font-size: var(--t-fine);
  color: var(--ink-faint);
  max-inline-size: var(--measure);
  margin: 0 0 var(--gap-tight);
}

/* Sent and came back, side by side, because the question is nearly always what we asked for
   against what we got. They stack on a narrow screen rather than shrinking to unreadable. */
.call__sides {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--gap);
  padding-block-end: var(--gap);
}

.side {
  min-inline-size: 0;
}

.side__headers,
.side__body {
  font-size: var(--t-fine);
  background: var(--paper-sunk);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius);
  padding: var(--gap-tight);
  margin: var(--gap-tight) 0 0;
  max-block-size: 22rem;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.side__headers {
  color: var(--ink-faint);
}

/* --- bringing the existing centres across ----------------------------------
   This replaces hand-editing a JSON file on a server, so the screen is deliberately slower than
   the file was: paste, read, look at what would happen, then confirm. The preview reuses the
   centre rows from the list, because a row that looks different from where it will end up is a
   row somebody has to translate in their head. */

.paste {
  margin-block-start: var(--gap-section);
}

.paste__form {
  display: grid;
  gap: var(--gap-tight);
  justify-items: start;
}

.paste__field {
  inline-size: 100%;
  font-size: var(--t-fine);
  padding: var(--gap-tight);
  color: var(--ink);
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius-control);
  resize: vertical;
}

/* The confirm step posts the same text back rather than holding it on the server, so what is
   imported is what was previewed. It is a field, not a hidden input, because a textarea bound
   by the framework has to exist to round-trip - and it is taken out of the flow rather than out
   of the document, so assistive technology can still reach it. */
.paste__hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.paste__note {
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
  margin-block: var(--gap) 0;
}

/* A centre in the preview is the same row as in the list, with the two extra things this screen
   has to say: where its invoices would go, and why one is being left out. */
.centre--skipped {
  background: var(--plate-xero-wash);
}

.centre--skipped .centre__state {
  color: var(--plate-xero);
}

.centre--ready .centre__state {
  color: var(--registered);
}

.centre__note,
.centre__problem {
  grid-column: 1 / -1;
  font-size: var(--t-fine);
  max-inline-size: var(--measure);
}

.centre__note {
  color: var(--ink-faint);
}

.centre__problem {
  color: var(--ink);
}

/* --- what is wrong ---------------------------------------------------------
   The same docket as the doctor, because it is the same kind of thing: something that needs
   doing, with what to do about it. The one addition is a way to say "we know, and here is why
   we are living with it" - because a queue with no way to close a row is a queue that grows
   until nobody opens it. */

.dismiss {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap-tight);
  padding-block-start: var(--gap-tight);
  border-block-start: 1px solid var(--rule-soft);
  margin-block-start: var(--gap-tight);
}

.dismiss .label {
  flex: 0 0 auto;
}

/* Grows into the row rather than sitting at a fixed width, so the reason has room to be a
   sentence instead of a shrug. */
.dismiss__why {
  flex: 1 1 18rem;
  inline-size: auto;
  font-family: var(--sans);
  font-size: var(--t-fine);
}

/* The dismiss button is .button--quiet, defined with the other buttons above: dismissing is not
   the action this screen wants somebody to take first. */

.verdict--settled {
  border-inline-start-color: var(--registered);
}

/* A fact sitting where a status would, and not to be read as one. Same position in the row as
   .check__state, none of its colour. */
.check__aside {
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
  text-align: end;
}

/* --- the gate -------------------------------------------------------------
   The sign-in screen, and the only one somebody sees before they are anybody. It carries the
   wordmark and nothing else: no nav, because every link would bounce back here, and no
   reassurance, because a page that explains itself at length is a page that looks unsure.

   The card sits on the ground colour rather than filling the page, so the first impression of
   the tool is the same plate-on-paper language every screen after it uses. */

.gate {
  min-block-size: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--gap-wide);
  background: var(--ground);
}

/* The same card on a screen that already has a masthead - changing a password, which is the one
   account screen somebody reaches while signed in. */
.gate--inline {
  min-block-size: 0;
  place-items: start;
  background: none;
  padding: 0;
  margin-block-start: var(--gap-section);
}

.gate__card {
  inline-size: min(26rem, 100%);
  padding: var(--gap-section) var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);

  /* The registration offset, once, on the one element that is nothing but itself. Two plates
     not quite aligned is what this whole tool is about. */
  box-shadow: var(--misregister) var(--misregister) 0 0 var(--plate-psv-wash),
              calc(var(--misregister) * -1) calc(var(--misregister) * -1) 0 0 var(--plate-xero-wash);
}

.gate__mark {
  font-family: var(--mono);
  font-size: var(--t-title);
  font-weight: 600;
  letter-spacing: 0.04em;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.4em;
  margin-block-end: var(--gap-section);
}

/* Under the wordmark and quieter than it: the name says who is asking, and the wordmark says
   what is about to happen. Reversing that emphasis would make the page an advertisement. */
.gate__brand {
  margin-block: calc(var(--gap-section) * -1 + var(--gap-hair)) var(--gap-section);
  font-size: var(--t-fine);
  letter-spacing: 0.04em;
  text-align: center;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.gate__form {
  display: grid;
  gap: var(--gap-hair);
}

.gate__form .label {
  margin-block-start: var(--gap);
}

.gate__form .label:first-child {
  margin-block-start: 0;
}

.gate__field {
  inline-size: 100%;
  font-family: var(--sans);
  font-size: var(--t-body);
  padding: 0.5rem 0.625rem;
  color: var(--ink);
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: var(--radius-control);
}

.gate__field:focus-visible {
  outline: 2px solid var(--plate-psv);
  outline-offset: 1px;
}

.gate__form .button {
  margin-block-start: var(--gap-wide);
  justify-self: stretch;
  text-align: center;
}

.gate__note,
.gate__nobody {
  margin-block: var(--gap) 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

/* A refusal is the one thing on this screen somebody has to read, so it is the one thing that
   is not grey. Not red either: being refused once is ordinary, and a page that shouts at
   somebody for mistyping a password is a page that makes them doubt the password. */
.gate__refused {
  margin-block: var(--gap-wide) 0;
  padding: var(--gap-tight) var(--gap);
  font-size: var(--t-fine);
  color: var(--ink);
  background: var(--plate-xero-wash);
  border-inline-start: 3px solid var(--plate-xero);
  border-radius: var(--radius);
}

/* --- who is signed in -----------------------------------------------------
   In the masthead, pushed to the far end. It answers a question nobody asks until it matters:
   whose account is this, on a machine several people use. */

.badge {
  margin-inline-start: auto;
  display: flex;
  align-items: baseline;
  gap: var(--gap-tight);
  white-space: nowrap;
}

.badge__who {
  font-size: var(--t-fine);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  border-block-end: 1px solid transparent;
}

.badge__who:hover {
  border-block-end-color: var(--rule);
}

.badge__role {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.badge__out {
  display: inline;
}

/* --- people ---------------------------------------------------------------
   One row per account. What they may do is spelled out rather than named, because "Operator"
   means nothing to somebody deciding what to give a new starter. */

.people {
  display: grid;
  gap: var(--gap-tight);
}

.who {
  display: grid;

  /* Five, one per thing a row says. Four left the last item on a row of its own, where it
     stretched across a track meant for an address. */
  grid-template-columns: minmax(9rem, 1fr) minmax(11rem, 1.2fr) auto auto auto;
  gap: var(--gap-hair) var(--gap);
  align-items: baseline;
  padding: var(--gap) var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-inline-start: 3px solid var(--registered);
  border-radius: var(--radius);
  color: inherit;
  text-decoration: none;
}

a.who:hover {
  border-color: var(--rule);
  border-inline-start-color: var(--plate-psv);
}

/* An account that cannot sign in reads as settled rather than as broken: it is turned off
   because somebody meant to turn it off. */
.who--off {
  border-inline-start-color: var(--rule);
  color: var(--ink-dim);
}

.who__head {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap);
}

.who__name {
  font-weight: 600;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap-tight);
}

.who__email {
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

.who__role {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.who__state,
.who__seen {
  font-size: var(--t-fine);
  color: var(--ink-dim);
  text-align: end;
}

.who__can {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* --- the trail ------------------------------------------------------------
   Who did what, in the order it happened. Three columns because those are the three things
   somebody reads: when, who, and what they did. */

.entry--said,
.entries .entry:has(.entry__who) {
  grid-template-columns: auto auto auto 1fr;
}

.entry__when {
  font-size: var(--t-fine);
  color: var(--ink-faint);
  white-space: nowrap;
}

.entry__who {
  font-size: var(--t-fine);
  font-weight: 600;
  white-space: nowrap;
}

.entry__email {
  font-size: var(--t-fine);
  color: var(--ink-faint);
  white-space: nowrap;
}

/* --- the watch -----------------------------------------------------------
   Whether anything is looking, said before anything it found.

   The line that decides whether the rest of the front page means anything. A dashboard whose
   figures are a day old looks exactly like one whose figures are current, so the state of the
   watch gets its own block rather than a line in the tally - and the block changes colour,
   because the one thing somebody must not have to read carefully is whether to trust the page. */

.watch {
  margin-block-start: var(--gap-wide);
  padding: var(--gap) var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-inline-start: 3px solid var(--rule);
  border-radius: var(--radius);
}

/* Looking, and up to date. */
.watch--on { border-inline-start-color: var(--registered); }

/* Never turned on. Not a fault - a deployment without a read API user per centre is right not
   to sweep - so it reads as a note rather than as something broken. */
.watch--off { border-inline-start-color: var(--rule); }

/* Turned on and gone quiet, which is the one state this block exists to shout about. */
.watch--fix { border-inline-start-color: var(--plate-xero); }

.watch__line {
  margin: var(--gap-hair) 0 0;
  font-size: var(--t-figure);
}

.watch__when,
.watch__found {
  margin: var(--gap-hair) 0 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

.watch__found a {
  color: inherit;
}

.watch__stale {
  margin: var(--gap) 0 0;
  padding: var(--gap-tight) var(--gap);
  font-size: var(--t-fine);
  background: var(--plate-xero-wash);
  border-radius: var(--radius);
}

.watch__note {
  margin: var(--gap-tight) 0 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* Folded away by default.

   The two paragraphs under it are exactly what somebody setting this up needs, and exactly
   what somebody glancing at this page every morning does not. Leaving them open would have a
   third of the dashboard permanently occupied by an explanation nobody is reading any more. */
.watch__more {
  margin-block-start: var(--gap-tight);
  font-size: var(--t-fine);
}

.watch__more > summary {
  cursor: pointer;
  color: var(--ink-dim);
  inline-size: fit-content;
}

.watch__more > summary:hover {
  color: var(--ink);
}

/* The note under the intended lines, explaining a column of zeroes that is telling the truth. */
.lines__note {
  margin: var(--gap-tight) 0 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
}

/* --- what is running, and moving off the old system ------------------------
   Two screens, one vocabulary. Both are lists somebody works down rather than dashboards they
   interpret, so the rules here are about making a row scannable and an action unmistakable -
   not about decoration.

   The inks are the ones already in use: registered green for agreement, Xero magenta for
   something wrong, PrintSmith blue for something worth noting. A reader who cannot tell them
   apart loses nothing, because every one of them is also worded. */

/* No mark. A centre nobody has set up yet is not a state worth colouring - and fifteen of
   them on import day would be a page of alarm meaning nothing. */
.entry--plain,
.watch--plain { border-inline-start-color: var(--rule); }

.entry--ready { border-inline-start-color: var(--registered); }
.entry--fix { border-inline-start-color: var(--plate-xero); }
.entry--note { border-inline-start-color: var(--plate-psv); }

/* The row's head: name, state, its own clock, and which system owns it. One line on a desk,
   four on a phone. */
.entry__head {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap-tight) var(--gap);
}

.entry__name {
  font-size: var(--t-title);
  font-weight: 600;
  color: inherit;
  text-decoration: none;
}

.entry__name:hover {
  text-decoration: underline;
}

/* Its own clock. Quiet, because it is context for the state rather than the state - but
   present, because "closed for the day" means nothing without the time it is closed at. */
.entry__when,
.entry__owner {
  font-family: var(--mono);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Pushed to the end of the line, so a column of them reads down the right-hand edge and the
   one centre still on the old system is findable without reading the others. */
.entry__owner {
  margin-inline-start: auto;
}

.entry__line {
  grid-column: 1 / -1;
  margin: var(--gap-hair) 0 0;
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* What to do about it. Only ever present when there is something to do, so its presence is
   itself the signal - a row with no remedy needs nothing. */
.entry__remedy {
  grid-column: 1 / -1;
  margin: var(--gap-tight) 0 0;
  padding-inline-start: var(--gap);
  border-inline-start: 2px solid var(--plate-xero);
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

.entry__act {
  grid-column: 1 / -1;
  margin: var(--gap-tight) 0 0;
}

/* What just happened, said once. Placed above the list rather than beside the button, because
   after a post the page has re-rendered and the button somebody pressed may have moved. */
.said {
  margin: 0 0 var(--gap-section);
  padding: var(--gap) var(--gap-wide);
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  border-inline-start: 3px solid var(--registered);
  border-radius: var(--radius);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* --- a headed group of rows, one per centre -------------------------------
   Somebody looking at refusals is looking at one centre's, so the page is grouped by centre and
   each group says whose it is. A flat list with a centre column is the same information and a
   worse shape for the question being asked of it. */

.pile {
  margin: 0 0 var(--gap-section);
}

/* The name and what the group came to, on one line while there is room for one. */
.pile__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-tight);
  align-items: baseline;
  justify-content: space-between;
}

.pile__name {
  font-size: var(--t-title);
  font-weight: 600;
  margin: 0 0 var(--gap-hair);
}

.pile__note {
  margin: 0 0 var(--gap);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* --- setting one centre up ------------------------------------------------
   A sequence, shown as one. The step somebody is up to is open and everything else is a line:
   a number or a tick, what the step is, and what it came to. A page that opened every
   unfinished form at once would be the page this replaced.

   The number column is fixed so the ticks and the numerals line up down the left edge, which
   is what makes "where am I" answerable without reading a word. */

.steps-of {
  list-style: none;
  margin: var(--gap) 0 0;
  padding: 0;
  display: grid;
  gap: var(--gap-tight);
}

.stage {
  /* A grid child defaults to the width of its widest unbreakable word, and a pasted address
     is one. */
  min-inline-size: 0;
}

.stage__box {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-inline-start: 3px solid var(--rule);
  border-radius: var(--radius);
}

/* Done recedes, the current one is stated, the ones ahead are quiet. Three weights rather
   than three colours: this page is a sequence, not a diagnosis. */
.stage--done > .stage__box { border-inline-start-color: var(--registered); }
.stage--now  > .stage__box { border-inline-start-color: var(--plate-psv); }
.stage--later > .stage__box { opacity: 0.72; }

.stage__head {
  display: grid;
  grid-template-columns: 1.75rem 1fr auto;
  gap: var(--gap-tight) var(--gap);
  align-items: baseline;
  padding: var(--gap) var(--gap-wide);
  cursor: pointer;
}

.stage__at {
  font-family: var(--mono);
  font-size: var(--t-fine);
  color: var(--ink-faint);
}

.stage--done .stage__at { color: var(--registered); }

.stage__what {
  font-weight: 600;
}

.stage--done .stage__what { font-weight: 400; color: var(--ink-dim); }

/* What the step came to - the API user, the organisation, the date. The thing worth seeing
   on a folded line, and the thing a row of ticks never showed. */
.stage__now {
  font-family: var(--mono);
  font-size: var(--t-fine);
  color: var(--ink-faint);
  overflow-wrap: anywhere;
}

.stage__body {
  padding: 0 var(--gap-wide) var(--gap-wide);
  grid-column: 1 / -1;
}

.stage__why,
.stage__says {
  margin: 0 0 var(--gap);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

.stage__why {
  font-size: var(--t-fine);
}

/* Disconnecting sits under the thing it undoes, quiet and separated by a rule - reachable
   without being the first thing the eye lands on. */
.undo {
  margin-block-start: var(--gap-wide);
  padding-block-start: var(--gap);
  border-block-start: 1px solid var(--rule-soft);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--gap-tight) var(--gap);
}

.undo__note {
  font-size: var(--t-fine);
  color: var(--ink-faint);
  max-inline-size: var(--measure);
}

/* The numbered steps that take a centre from registered to syncing. */
.setup {
  margin-block-start: var(--gap-section);
}

/* An act that should take a moment: removing a centre, or giving up on a document. Folded
   away, and deliberately not compact - a form somebody can complete without reading is the
   wrong shape for either of them. */
.deliberate__confirm {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--gap-tight);
  align-items: start;
  max-inline-size: var(--measure);
  cursor: pointer;
}

.deliberate__confirm input {
  inline-size: 1.1rem;
  block-size: 1.1rem;
  margin-block-start: 0.15rem;
}

.deliberate__why {
  margin: 0;
  font-size: var(--t-fine);
  color: var(--ink-dim);
  max-inline-size: var(--measure);
}

/* Above the box, not beside it. A label is inline by default, so a long caption and a field
   with room left over sit on one line and read as two unrelated things. */
.deliberate .label {
  display: block;
  margin-block-end: var(--gap-tight);
}

/* What is being given up on, in the person's own words. Required, so it is not quiet. */
.deliberate__reason {
  inline-size: 100%;
  max-inline-size: var(--measure);
  margin-block: var(--gap-tight);
  padding: var(--gap-tight);
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-tight);
}

/* Closed by default, because somebody scanning the page is not looking for it. */
.deliberate {
  grid-column: 1 / -1;
  margin: var(--gap-tight) 0 0;
}

.deliberate > summary {
  cursor: pointer;
  font-size: var(--t-fine);
  color: var(--ink-faint);
}

.deliberate > summary:hover {
  color: var(--ink-dim);
}

/* --- a phone ---------------------------------------------------------------
   Every row in this application is several columns of figures, which is right on a desk and
   impossible at 400 pixels. Below this they become one column each.

   Worth the rules: the person most likely to open this on a phone is the one with a franchisee
   on the line and no desk in front of them, and a page that scrolls sideways is how they
   conclude the tool is not for them. */

/* --- the footer -----------------------------------------------------------
   The screens that are real but not daily. Quiet on purpose: a link somebody needs once a month
   competing with the nav would cost the nav more than it gains the link. */

.footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap) var(--gap-wide);
  margin-block-start: var(--gap-section);
  padding-block-start: var(--gap);
  border-block-start: 1px solid var(--rule-soft);
}

/* The nav's face, so these read as the same kind of thing: somewhere to go. Not the nav's
   weight, because they are not where somebody goes first. */
.footer__link {
  font-family: var(--sans);
  font-size: var(--t-label);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
}

.footer__link:hover,
.footer__link:focus-visible {
  color: var(--ink);
  text-decoration: underline;
}

@media (max-width: 30rem) {
  /* The owner mark stops being pushed to the end of its line: on one column, "auto" would
     leave it alone on a line of its own with a gap above it. */
  .entry__owner {
    margin-inline-start: 0;
  }

  .centre,
  .who,
  .entries .entry,
  .entries .entry:has(.entry__who) {
    grid-template-columns: 1fr;
  }

  /* Figures that were right-aligned in their own column read as stray numbers once they are
     under the thing they belong to. */
  .centre__state,
  .centre__zone,
  .centre__owner,
  .who__role,
  .who__state,
  .who__seen,
  .entry__state {
    text-align: start;
  }

  /* These were kept on one line to hold a column. There is no column now. */
  .entry__when,
  .entry__who,
  .entry__email,
  .badge {
    white-space: normal;
  }

  .nav {
    gap: var(--gap-hair) var(--gap-tight);
  }
}
