/*
 * The whole of the website's appearance. One file, no preprocessor, no framework.
 *
 * It is loaded as a stylesheet and never inlined, because the site's Content-Security-Policy
 * allows `style-src 'self'` and nothing else. A view that needed an inline style would not
 * render, which is deliberate: the same rule is what stops a script getting onto a page that
 * holds a Token.
 *
 * Both colour schemes, from one set of custom properties. The system decides. Nothing below
 * this token block names a colour: a rule that hardcoded one would be a rule that is right in
 * one scheme and wrong in the other, and the two places the prototype did that — the floating
 * bar and the fade above the box at the bottom of a Round — are `--veil` and `--fade` here.
 *
 * **Colour is never the only signal.** The agent's Messages and the Party's are told apart by
 * side, by colour and by the word above them; a Dispute's state is a labelled fact, not a
 * coloured edge. Every rule here that uses colour to mean something has a word doing the same
 * job next to it.
 */

:root {
  color-scheme: light dark;

  --bg: #f5f6f8;
  --card: #ffffff;
  --ink: #13171f;
  --quiet: #6b7280;
  --line: #e6e8ec;
  --accent: #4f46e5;
  --accent-ink: #ffffff;
  --accent-soft: #eef0fe;
  --accent-line: #d7d4fd;
  --amber: #b45309;
  --amber-soft: #fef6e7;
  --amber-line: #f3e0bb;
  --alarm: #b3261e;
  --alarm-ink: #ffffff;

  /* One soft two-layer shadow, everywhere. A second one would be a second idea. */
  --shadow: 0 1px 2px rgba(17, 24, 39, 0.05), 0 8px 24px -12px rgba(17, 24, 39, 0.12);
  --shadow-lifted: 0 24px 60px -20px rgba(17, 24, 39, 0.45);
  --backdrop: rgba(19, 23, 31, 0.4);

  /* The bar on the Round page floats over the conversation, so it is the ground half-drawn. */
  --veil: rgba(255, 255, 255, 0.82);
  /* What the conversation fades into under the box at the bottom of a Round. */
  --fade: rgba(245, 246, 248, 0.94);

  --radius: 14px;
  --radius-small: 10px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1116;
    --card: #171a21;
    --ink: #e7e9ee;
    --quiet: #99a1af;
    --line: #262b34;
    /* Lightened until it carries white-on-accent text and reads as the same one indigo. */
    --accent: #8b85f5;
    --accent-ink: #10121a;
    --accent-soft: #20203a;
    --accent-line: #383567;
    --amber: #e0a24a;
    --amber-soft: #251c10;
    --amber-line: #4a3a1e;
    --alarm: #f08379;
    --alarm-ink: #1a0f0d;

    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px -12px rgba(0, 0, 0, 0.7);
    --shadow-lifted: 0 24px 60px -20px rgba(0, 0, 0, 0.8);
    --backdrop: rgba(6, 8, 12, 0.6);

    --veil: rgba(23, 26, 33, 0.82);
    --fade: rgba(15, 17, 22, 0.94);
  }
}

* {
  box-sizing: border-box;
}

/*
 * `hidden` wins over everything, and this rule is the reason it does.
 *
 * A view hides things by setting the property — a composer on a refused page, the sentence above a
 * shut one, the choice a Round opens on — and the browser's own `[hidden] { display: none }` is a
 * rule of the lowest possible weight. Any `display` in this file, on the element itself or through a
 * class it happens to carry, silently beats it, and what a reader gets is an empty card where
 * nothing should be. Every hiding on the site goes through this one line rather than through a
 * second class each view has to remember.
 */
[hidden] {
  /* biome-ignore lint/complexity/noImportantStyles: reversing the cascade is the point — see above. */
  display: none !important;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font:
    15px / 1.6 -apple-system,
    system-ui,
    "Segoe UI",
    sans-serif;
  -webkit-font-smoothing: antialiased;
}

#page {
  max-width: 40rem;
  min-height: 100vh;
  margin: 0 auto;
  padding: 1.5rem 1rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

h1 {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 650;
  letter-spacing: -0.02em;
}

.banner {
  font-weight: 700;
  letter-spacing: -0.02em;
}

/*
 * The wordmark in the bar, which is the way home and therefore a link.
 *
 * **It keeps the ink it had as a word.** `a` above paints every link with the one accent, and the
 * name of the site in accent would make it the loudest thing on a page it is only the corner of: it
 * is the wordmark before it is a control, and it should read as one. `inherit` rather than
 * `var(--ink)` so it is the colour of the text around it in whichever scheme is on, which is what it
 * was before it became a target.
 *
 * The underline on hover is the site's own signal for a link and is left alone: without it there
 * would be nothing at all saying the word can be pressed.
 */
a.banner {
  color: inherit;
}

.quiet {
  color: var(--quiet);
}

/*
 * Somebody's own words, wherever they are shown. `pre-wrap`: a Party's paragraphs and line breaks
 * are theirs, and are shown as written.
 *
 * Here rather than beside the Messages, because the rules that tint one — a bubble, a Verdict —
 * are all more specific than this and all come later.
 */
.said {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/*
 * A short line about the thing above it: what happened to a Message, what a Handle has to look
 * like, what an Invite link cannot do twice. The base is here for the same reason `.said`'s is.
 */
.state {
  margin-top: 0.25rem;
  font-size: 0.8rem;
  color: var(--quiet);
}

/*
 * The line under a heading that says what the page is about and where it sits.
 *
 * On the Round page it is also the one link up (ui-revamp ticket 04): the bar carries the wordmark
 * home to the list and nothing between, so "Dispute with ilse" is where the level in between lives,
 * saying both where you are and where up is in one line.
 */
.standfirst {
  margin: 0;
  color: var(--quiet);
}

.standfirst a {
  font-weight: 600;
}

/*
 * The heading over a list or a block, which is a label rather than a title: small, spaced and
 * quiet, so the thing under it is what the eye lands on.
 */
.aside-heading {
  margin: 1rem 0 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--quiet);
}

/* ------------------------------------------------------------------- the chrome */

/*
 * The chrome is one node a page appends and no layout of its own: `display: contents` puts the
 * header row and the line a refused Log out is said on straight into the page's own column, with
 * the column's own gaps between them (ui-revamp ticket 04).
 */
.chrome {
  display: contents;
}

/*
 * The bar: the wordmark, which is the way home, who is signed in, and Log out. A card like
 * everything else on the page rather than a rule across it, so the page reads as things on a ground.
 */
.bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
  padding: 0.6rem 0.8rem;
  background: var(--card);
  border-radius: 12px;
  box-shadow: var(--shadow);
}

/*
 * Who is signed in, between the wordmark and the controls.
 *
 * It truncates rather than wrapping. `overflow-wrap: anywhere` was the obvious way to stop a long
 * Handle pushing the buttons off a narrow bar, and it is the wrong one: it takes the flex item's
 * minimum width down to one character, so on a phone the bar squeezed the Handle into a column of
 * single letters beside the buttons. A Handle is thirty-two characters at most and the bar is not
 * where anybody reads it, so the end of a long one is what gives way.
 */
.handle {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--quiet);
  font-size: 0.9rem;
}

/* -------------------------------------------------------------------- cards */

.card {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.2rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

/*
 * Air above a card that follows the line under a heading directly, which today is the log-in page
 * and nothing else: a wordmark, one line saying what this is, and then the form.
 *
 * The page's column gap is 0.75rem, which is the space between things that belong together, and
 * the form does not belong to the sentence above it: it is the next thing, after being told what
 * the site is. So it takes the break `.aside-heading` puts above a section (its own 1rem on top of
 * the gap) and a little more, this being the first page a returning User sees and the card being
 * the only thing on it.
 */
.standfirst + .card {
  margin-top: 1.5rem;
}

/*
 * Labelled facts, wherever they are: the block at the top of a Dispute page, and the state and the
 * length of a Dispute on a row of the list (ui-revamp ticket 07). Two columns rather than prose.
 *
 * The label column is `auto`, so it is as wide as "Respondent" and no wider, and the value column
 * is `minmax(0, 1fr)`, so it may shrink to nothing and wrap instead of pushing the card wider than
 * the page. That pair is what keeps a long Handle from giving the body a horizontal scrollbar on a
 * narrow screen; `overflow-wrap` on the value is the other half, for a Handle with no space in it
 * to break at.
 *
 * Baselines are aligned rather than boxes, because a one-word label beside a value that wraps to
 * two lines reads as a label for it only if the two start on the same line.
 *
 * **One class and not two copies of a grid**, because the two places these appear are a list and
 * the page a row of it goes to, and a reader moving between them is reading the same two facts. Up
 * here with the cards and the fields rather than in either page's section, for the same reason. What
 * differs is the card around the Dispute page's block, which is `.facts` in that section.
 */
.labelled {
  margin: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: 1.1rem;
  row-gap: 0.5rem;
}

/*
 * The label: quiet and small, so the column of them reads as the spine of the block rather than as
 * half the words on it. Same treatment as `.aside-heading`, which is the other label on this page.
 */
.labelled dt {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--quiet);
}

/* The fact itself, in ink: it is what somebody came to read. */
.labelled dd {
  margin: 0;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/*
 * Narrow enough that a label column would leave the values a few words wide: the value goes under
 * its label instead of beside it. One column, and the pairs kept apart by the space under each
 * value rather than by the row gap, so a label still sits with the fact it labels.
 */
@media (max-width: 26rem) {
  .labelled {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0;
  }

  .labelled dd:not(:last-child) {
    margin-bottom: 0.7rem;
  }
}

.label {
  margin-top: 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--quiet);
}

.field {
  padding: 0.6rem 0.7rem;
  font: inherit;
  color: inherit;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-small);
}

.field:focus-visible,
.compose:focus-visible,
.primary:focus-visible,
.plain:focus-visible,
a:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/*
 * A refusal. It is empty until there is one, and `:empty` keeps it from reserving a line, so
 * nothing on the page moves when one appears above the fold.
 *
 * **Every refusal on every page is this one rule** (story 67): a wrong password, a Gateway that
 * did not answer, a Handle that is taken, a link that names no Dispute. It is a sentence in the
 * page's own column, on an alarm-coloured card, and it is never a colour on its own — the words
 * are the whole of it and the card is only where they sit.
 */
.refusal {
  margin: 0.25rem 0 0;
  padding: 0.7rem 0.85rem;
  color: var(--alarm);
  font-weight: 550;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--alarm);
  border-radius: var(--radius-small);
}

.refusal:empty {
  display: none;
}

/* -------------------------------------------------------------------- buttons */

.primary,
.plain {
  font: inherit;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-small);
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}

.primary {
  background: var(--accent);
  color: var(--accent-ink);
  border: 1px solid var(--accent);
  font-weight: 600;
}

/*
 * Air above the button that submits a card of fields, which is the log-in form and the one that
 * accepts an Invite.
 *
 * The card's own gap is 0.4rem, which is the space between a label and the field under it. The
 * button is not the next row of the form: it is what a person does once the rows are filled, and at
 * the form's own gap it sat against the last field as though it were another one.
 */
.card > .primary {
  margin-top: 0.6rem;
}

.plain {
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--line);
}

/*
 * A control that is a page's own act rather than a row in a form, which is the page's one column
 * and would otherwise be stretched the width of it. A button as wide as a paragraph reads as a
 * banner rather than as a thing to press.
 */
a.plain,
#page > .primary,
#page > .plain {
  align-self: flex-start;
}

.primary:hover:not(:disabled),
.plain:hover:not(:disabled) {
  text-decoration: none;
  filter: brightness(0.97);
}

.primary:disabled,
.plain:disabled {
  opacity: 0.45;
  cursor: default;
}

/*
 * The one act with no way back, wherever it is offered. Alarm-coloured and never the button the
 * eye lands on first: it sits beside a Cancel that is the ordinary one.
 */
.danger {
  background: var(--alarm);
  color: var(--alarm-ink);
  border-color: var(--alarm);
}

/* ------------------------------------------------------- the root page's Disputes */

/*
 * The page a Party lands on is a list, so it is the shell's one column and nothing beside it. The
 * conversation left it with the chat page (ui-revamp ticket 03): what is here is what is waiting,
 * and one link into the Round the reader is in the middle of.
 */

/*
 * The Round they are in the middle of, above the lists.
 *
 * The loudest thing on the page, because it is what somebody halfway through telling the agent what
 * happened has come back for, and everything else here is a list they would have to read first.
 */
.carryon {
  display: block;
  padding: 1rem 1.1rem;
  color: var(--accent-ink);
  background: var(--accent);
  border-radius: var(--radius);
  font-weight: 600;
  font-size: 1.05rem;
  box-shadow: 0 10px 24px -14px var(--accent);
}

.carryon:hover {
  text-decoration: none;
  filter: brightness(1.05);
}

.carryon[hidden] {
  display: none;
}

.disputes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/*
 * One Dispute.
 *
 * The tint on the one the agent is hearing the reader in is the second signal only: the words at
 * the bottom of the row say it, "Carry on with this Round" against "Read this Dispute", and they
 * survive a screen reader and a person who does not see colour. That line used to be backed up by a
 * chip reading "The agent hears you here", which the owner removed with the rest of the row's prose
 * (ui-revamp ticket 07).
 */
.dispute {
  display: flex;
  padding: 0.9rem 1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: 0.93rem;
}

.dispute.yours {
  border-color: var(--accent-line);
  background: linear-gradient(0deg, var(--card), var(--accent-soft));
}

/* The whole row is one link, so it reads as the row rather than as a word in it. */
.dispute > a {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  color: inherit;
}

.dispute > a:hover {
  text-decoration: none;
}

.dispute p {
  margin: 0;
}

/* Who the Dispute is with, which is the first thing a row says and the loudest. */
.dispute .who {
  font-weight: 600;
  font-size: 1rem;
}

/*
 * The two facts on a row, tighter than the same pairs are on a Dispute page: a row is scanned in a
 * list of rows, and the block at the top of a page is read on its own.
 */
.dispute .labelled {
  margin: 0.15rem 0 0.1rem;
  column-gap: 0.8rem;
  row-gap: 0.15rem;
  font-size: 0.9rem;
}

/*
 * The act of opening a Dispute: a field for a Handle and the button under it, in a section of its
 * own below the lists.
 *
 * **The warning and the tick above the field are gone** (ADR-0031). It carried three paragraphs
 * saying that a Dispute cannot be withdrawn and a checkbox that had to be ticked before the button
 * worked; the owner removed both. The act is still the one here that cannot be undone (ADR-0005),
 * and what is left of saying so is the sentence the page answers with once one is open, below.
 */
.opening {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 1.1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.opening .primary,
.inviting .primary {
  align-self: flex-start;
}

/*
 * The other act at the bottom of the page: making an Invite, in the same box (ui-revamp ticket 07).
 *
 * The two acts a User has are the two things under the lists, and this one was a heading and a loose
 * button while the other was a panel. Same rules as `.opening` and not a shorthand for both, because
 * they are two sections that happen to look alike rather than one thing drawn twice: this one holds a
 * button and the line a refused request is said on, and nothing else ever will — the links it makes
 * are cards of their own below it.
 */
.inviting {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 1.1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

/*
 * The two things this button can be: an Invite, and an Invite that carries a Dispute.
 *
 * A row, because it is one choice between two presses rather than two acts stacked. It wraps, the
 * second label being a whole sentence and the column being a phone's width on a phone: two buttons
 * squeezed side by side would be two labels broken over three lines each.
 */
.inviting .acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

/* ------------------------------------------------------ an act with no way back */

/*
 * One block, drawn at both ends of the same kind of act.
 *
 * The line the root page answers with once a Dispute has been opened on it, which is said after the
 * act. This block was shared with the warning above a Pass and with the one above the field a
 * Dispute is opened from: opening lost its warning first (ADR-0031) and Passing lost its own with
 * the control that carried it (ADR-0032), so the tick that went with a warning has gone with them.
 *
 * **And the disclosure on the acceptance page, which is said before the act** (ADR-0036). It is the
 * one warning-before on this site, and it is here rather than reversing ADR-0031 for the reason
 * that ADR records: this is a stranger being told that opening a link makes them the Respondent in
 * a Dispute nobody can withdraw, not somebody being warned about an act they chose.
 *
 * Amber rather than alarm red: this is a thing done on purpose, not a thing that has gone wrong.
 * Red is kept for the refusals and for the button that ends a Dispute.
 */
.notice.opened {
  padding: 1rem 1.1rem;
  background: var(--amber-soft);
  border: 1px solid var(--amber-line);
  border-left: 4px solid var(--amber);
  border-radius: var(--radius);
  color: var(--amber);
}

/* ------------------------------------------------------------ the conversation */

/*
 * The Messages, the composer and the line under them. These were the chat page's and are the Round
 * page's now: the chat page is gone and the conversation is one Round of one Dispute
 * (ui-revamp ticket 03).
 */

/*
 * The conversation is not a pane. It has no scroller of its own: the page is the scroll, so the
 * Messages get the whole height of the window rather than a short box between two fixed ends, and
 * the box at the bottom stays on the screen by being sticky rather than by being fenced in.
 */
.conversation {
  display: flex;
  flex-direction: column;
}

.earlier {
  display: flex;
  justify-content: center;
  padding: 0.25rem 0 1rem;
}

.messages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

/*
 * A Message. Which side it sits on and which colour it takes is one half of telling the agent's
 * Messages from the Party's own; the word in `.author` is the other half, and it is the half
 * that survives a screen reader and a person who does not see colour.
 */
.message {
  display: flex;
  flex-direction: column;
  margin: 0 0 0.7rem;
  max-width: 84%;
}

.message.mine {
  align-self: flex-end;
  margin-left: auto;
  align-items: flex-end;
}

.message.theirs {
  align-self: flex-start;
}

.author {
  font-size: 0.72rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--quiet);
  margin-bottom: 0.2rem;
}

.message .said {
  padding: 0.6rem 0.85rem;
  border-radius: 16px;
  background: var(--card);
  border: 1px solid var(--line);
  border-bottom-left-radius: 5px;
  box-shadow: var(--shadow);
}

.message.mine .said {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
  border-bottom-left-radius: 16px;
  border-bottom-right-radius: 5px;
}

.message.sending .said {
  opacity: 0.6;
}

.message.unsent .said {
  background: var(--card);
  color: var(--ink);
  border-color: var(--alarm);
}

.message.unsent .state {
  color: var(--alarm);
}

.message.unsent .plain {
  margin-top: 0.35rem;
  padding: 0.3rem 0.7rem;
  font-size: 0.85rem;
}

/*
 * The one line under the conversation. It says a read or a send that failed, and nothing else.
 *
 * It used to say "the agent has not answered yet", and this comment used to say that the file
 * deliberately had no spinner in it, because a Run that died looks exactly like an agent that
 * is thinking and a spinner that never stops tells a person nothing. **That reversed**
 * (ADR-0008, amended): waiting is drawn as the indicator below, in the place the answer will
 * appear, with no timeout and no end but an answer. The observation was right and is now the
 * accepted cost rather than the reason.
 */
.notice {
  margin: 0;
  min-height: 1.5rem;
  font-size: 0.9rem;
  color: var(--quiet);
}

.notice.trouble {
  color: var(--amber);
  font-weight: 550;
}

/*
 * Waiting for something, wherever this site waits: under the last Message on a Round page,
 * where the agent's reply will appear, and under "The Verdict" on a Dispute page, where the
 * ruling will. One rule for both, because they are one kind of thing (`browser/view/waiting.ts`).
 *
 * **The word is the signal and the dots are decoration.** Motion says nothing to a screen
 * reader, nothing in a screenshot and nothing to somebody who has turned it off, so the
 * sentence beside the dots is always there and always says what is being waited for. It is the
 * same rule as colour never being the only signal, and it bites harder here.
 *
 * **Nothing stops it.** There is no timeout in this animation and none behind it: a Run that
 * died looks exactly like an agent that is thinking, and these dots will pulse either way,
 * forever. That is written down in ADR-0008 as the cost of drawing it this way.
 */
.waiting {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--quiet);
  font-size: 0.9rem;
}

/* `display` above beats `hidden`, so the one state that must win says so. */
.waiting[hidden] {
  display: none;
}

.waitingdots {
  display: inline-flex;
  gap: 0.25rem;
}

.waitingdots span {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  /* The colour of the sentence beside them, in whichever scheme is on. */
  background: currentColor;
  animation: waiting-pulse 1.2s ease-in-out infinite;
}

.waitingdots span:nth-child(2) {
  animation-delay: 0.15s;
}

.waitingdots span:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes waiting-pulse {
  0%,
  80%,
  100% {
    opacity: 0.25;
    transform: scale(0.85);
  }
  40% {
    opacity: 1;
    transform: scale(1);
  }
}

/*
 * Motion off, at the reader's own request. The dots stay, still, at one steady weight, and the
 * sentence beside them is untouched: what is being waited for is said in words whether anything
 * moves or not, which is the whole reason the words are there.
 */
@media (prefers-reduced-motion: reduce) {
  .waitingdots span {
    animation: none;
    opacity: 0.55;
  }
}

/*
 * On a Round page it is where the agent's reply will be, so it is drawn in the shape the reply
 * will be drawn in: the agent's bubble, on the agent's side, under the word saying whose it is.
 */
.message .waiting {
  padding: 0.6rem 0.85rem;
  border-radius: 16px;
  background: var(--card);
  border: 1px solid var(--line);
  border-bottom-left-radius: 5px;
  box-shadow: var(--shadow);
}

/*
 * The composer, as a card the box sits inside rather than as a bordered box with a button beside
 * it: the whole card is the thing you write in.
 */
.composer {
  display: flex;
  flex-direction: column;
  /* Between the box somebody writes in and the row the send button is on. The owner asked for
     slightly more of it (ui-revamp ticket 07), a button that close to the text reading as part of
     it. */
  gap: 0.65rem;
  padding: 0.75rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: var(--shadow);
}

.compose {
  width: 100%;
  padding: 0.5rem 0.6rem;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  border-radius: var(--radius-small);
  resize: vertical;
  outline: none;
}

.composer .send {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 1rem;
  padding: 0 0.2rem;
}

/*
 * A composer nothing would hear, greyed so that it is plainly not available while still being
 * visible: a person needs to see the box they cannot type in for the sentence above it to mean
 * anything.
 */
.composer:has(.compose:disabled) {
  background: var(--bg);
  box-shadow: none;
}

.compose:disabled {
  color: var(--quiet);
  cursor: not-allowed;
}

/* ------------------------------------------------------------ the Round page */

/*
 * The Round page holds the viewport, and the way it does that is the one piece of layout in this
 * file worth reading twice.
 *
 * **The page itself is the scroll.** The bar is fixed over it, so the conversation gets the whole
 * height of the window instead of a pane between two fixed ends.
 *
 * **The box at the bottom is sticky and not fixed**, and the difference is the whole of the
 * behaviour. A Round with a few Messages in it does not scroll, so the box stays where it falls,
 * directly under the last thing said. A Round long enough to run out of room does scroll, and the
 * box pins itself to the bottom of the window until the reader reaches the end of the
 * conversation. Nothing measures anything to decide which; the two cases fall out of `sticky`.
 *
 * The fade above it is not clickable, so a Message underneath can still be selected.
 */
#page.roundpage {
  display: block;
  padding-top: 5.5rem;
  padding-bottom: 0.75rem;
}

#page.roundpage .bar {
  position: fixed;
  z-index: 6;
  top: 0.75rem;
  left: 1rem;
  right: 1rem;
  max-width: 38rem;
  margin: 0 auto;
  background: var(--veil);
  backdrop-filter: saturate(1.6) blur(14px);
}

.roundhead {
  margin-bottom: 1.9rem;
}

.roundheading,
.disputeheading {
  margin: 0 0 0.25rem;
  font-size: 1.5rem;
  font-weight: 650;
  letter-spacing: -0.02em;
}

/*
 * Everything below the conversation: the line about what is being waited for, and whichever of the
 * three boxes this Round is showing.
 */
.roundfoot {
  position: sticky;
  z-index: 6;
  bottom: 0;
  padding: 2.25rem 0 0.85rem;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--fade) 45%, var(--bg) 70%);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.roundfoot > * {
  pointer-events: auto;
}

/*
 * The choice a Round opens on: the two moves a Party has (ui-revamp ticket 01).
 *
 * Before they have said anything in this Round the box is not a composer, because a composer is
 * only one of the two things they can do here and the other one ends the Dispute. Choosing to
 * engage swaps the composer in for good; it is an interface state and the control plane knows
 * nothing about it.
 *
 * **This block is also the whole of where a Pass is offered** (ADR-0032). The quiet control that
 * used to sit under the composer has gone, so once the composer is in there are no structured
 * controls left in the Round at all.
 */
.moves {
  padding: 1rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: var(--shadow);
}

.moves p {
  margin: 0 0 0.9rem;
}

.moves .row {
  display: flex;
  gap: 0.6rem;
  align-items: center;
}

/*
 * Pass, offered beside engaging and never as loudly. It is the irreversible one, so it is the quiet
 * control next to the ordinary one rather than the pair of them shouting equally.
 */
.moves .plain {
  background: none;
  color: var(--quiet);
  border-color: transparent;
  font-weight: 500;
}

.moves .plain:hover:not(:disabled) {
  background: var(--bg);
  color: var(--ink);
}

/*
 * The sentence above a composer that is shut, and where it points.
 *
 * Above the composer rather than below it, because it is about what would happen to what somebody
 * is about to type, and it has to be read before they type it.
 */
.roundclosed {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 1rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  color: var(--quiet);
}

.roundclosed .notice {
  color: inherit;
  font-size: inherit;
}

.roundclosed a {
  font-weight: 600;
}

/* ------------------------------------------------- the confirmation a Pass is taken behind */

/*
 * Passing is confirmed in a modal, because it is the one act on this page that ends the Dispute and
 * a modal is the one thing on a page that cannot be pressed past by accident.
 *
 * **One shape** (ADR-0032). There was a second, carrying the whole warning and a tick, for a Party
 * who had already made their case in this Round and was about to lose it (ADR-0027). A Pass is
 * offered only before they have said anything now, so what is left is two sentences and the two
 * buttons under them.
 */
.confirm {
  border: 0;
  border-radius: 16px;
  padding: 1.4rem;
  max-width: 28rem;
  width: calc(100% - 2rem);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  box-shadow: var(--shadow-lifted);
}

.confirm::backdrop {
  background: var(--backdrop);
}

.confirm h2 {
  margin: 0 0 0.6rem;
  font-size: 1.1rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.confirm p {
  margin: 0 0 0.9rem;
  color: var(--quiet);
  font-size: 0.95rem;
}

.confirm .row {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

.confirm .refusal {
  margin-bottom: 0.9rem;
}

/* ---------------------------------------------------------- the Dispute page */

/*
 * The Dispute page is a page for reading: who it is between, what it is waiting for, the ruling,
 * and then where the ruling came from. It has no composer and nothing on it is irreversible.
 *
 * **The Verdict comes before the Rounds** (ui-revamp ticket 02). It is why the page gets opened;
 * the Round history is where a Party goes afterwards to see where the ruling came from.
 */

/*
 * The block of labelled facts at the top of the page, as a card on the ground like everything else.
 * The grid inside it is `.labelled`, which the rows on the list wear too (ui-revamp ticket 07).
 */
.facts {
  padding: 1rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: 0.93rem;
}

.roundlist {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
}

.roundline {
  border-bottom: 1px solid var(--line);
}

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

/*
 * One Round in the history, which is a link and reads as one.
 *
 * The whole line is the target rather than a word in it, because it is a list of things to go to,
 * and every one of them is the same kind of thing.
 */
.roundline a {
  display: block;
  padding: 0.7rem 1.1rem;
  font-weight: 600;
}

/*
 * The other Party's Round: the same line, not a link, and the reason on the line after it. Quiet
 * rather than merely unlinked, because "you cannot go here" has to be visible before it is clicked.
 */
.roundline > span:first-child {
  display: inline-block;
  padding: 0.7rem 0 0.7rem 1.1rem;
  font-weight: 600;
  color: var(--quiet);
}

.roundline > .quiet {
  padding-right: 1.1rem;
  font-size: 0.88rem;
}

/*
 * A published Verdict, as a card on the Dispute it settles.
 *
 * A list of one, because the card was written for a record of many and the shape it wants is the
 * same either way: a block to be read rather than a row to be acted on.
 */
.record {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.verdictcard {
  gap: 0.7rem;
}

.verdictcard .bar {
  margin: 0;
  padding: 0;
  background: none;
  box-shadow: none;
}

.verdictcard .said {
  font-size: 1rem;
}

/*
 * The artifact, behind a `details` on the Verdict card.
 *
 * There were rules above these for what the check of a signature found, in four states with a
 * colour each. The website checks nothing now (ADR-0033), so the card is the ruling and this.
 */
.artifact {
  font-size: 0.9rem;
}

.artifact > summary {
  cursor: pointer;
  color: var(--quiet);
}

.artifact textarea {
  margin-top: 0.6rem;
  width: 100%;
  padding: 0.6rem;
  font:
    12px / 1.5 ui-monospace,
    monospace;
  word-break: break-all;
}

/* ------------------------------------------------------------ the Invites a User makes */

/*
 * The links made on this visit, in the box the button that made them is in. Each is a label and the
 * link under it, not a card: it was a card of its own, and a card inside the box of the act that
 * produced it is a border drawn twice around one thing.
 */
.invites {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* Nothing made yet is nothing to leave a gap for under the button. */
.invites:empty {
  display: none;
}

.invite {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.invite .author {
  margin: 0;
}

.invite .field {
  font-size: 0.9rem;
}

/*
 * What a link carrying a Dispute says, in the block this site says an act with no way back in.
 *
 * The same rule as the notice on the root page and the disclosure on the acceptance page, tightened
 * for a card inside a box: the amber block at its full padding, one of several down the column,
 * would be the loudest thing on the page. `min-height` goes with it, that being for a line that is
 * usually empty, and this one is drawn only when there is something to say.
 */
.invite .notice.opened {
  min-height: 0;
  padding: 0.7rem 0.8rem;
  font-size: 0.9rem;
}
