/* The neutrals are black and nothing else: no blue in the greys, so the only
   colour on the page is colour that means something. Everything above the page
   is a step up from black rather than a different grey — a panel, a field, a
   line — which is what keeps the depth readable without a single border being
   loud about it. */
:root {
  --bg: #000000;
  --panel: #0b0b0c;
  --panel-2: #151517;
  --line: #26262a;
  --text: #ededef;
  /* The second voice on every page — the sub-headline, the captions, the
     timestamps, the token counts. A clear step down from --text and no further:
     7.5 against the page here, 7.1 against paper below, so being the quieter
     voice does not cost it being read. */
  --muted: #9a9aa2;
  /* No hue at all, which is the rule at the top of this file taken at its word.
     If the only colour on the page is colour that means something, the accent —
     the one thing here that means nothing — cannot be the most saturated, so it
     takes the top of the lightness range and leaves the whole wheel to the
     three colours carrying a verdict. */
  --accent: #ededef;
  /* The accent with enough alpha left to spread a ring in. Not enough light to
     separate a finished phase of a validation from one not started, which is
     why .progress-step.done mixes its own. */
  --accent-soft: rgba(237, 237, 239, 0.42);
  /* What is legible on top of the accent: near-white here and near-black in the
     light scheme, so the arrow inside the send button flips rather than staying
     white and disappearing into it. Pure black, since an accent this close to
     grey has no hue to echo in its ink.

     Both are flat. Nothing in this file ramps the accent: at button size a
     diagonal from near-white to graphite reads as a bevel, and at the size of a
     letter it has no room to read as a ramp at all. What separates one surface
     from another is which flat shade of the one range it is set in. */
  --on-accent: #000000;
  --robust: #6cc58c;
  --partial: #d8b25f;
  --disputed: #e2707a;
  /* What the landing page's stage is filled with: the page's own ink at full
     strength, which is the one surface here that is not a step up from the
     ground. Every other panel in this file is lifted off the page by a shade
     — that is how a panel says it is above what it is on. The stage is not
     above the page, it is a piece of the page with the water taken out of it,
     and the two drawn rules top and bottom are what say where it starts and
     stops. A grey face would be a card lying on the drawing; a flat black one
     is a stretch of the drawing gone quiet. */
  --stage-face: #000000;
  /* The bar of a scrollbar, which is the one grey that is neither a surface nor
     a border: a step above the line colour, because a line is at rest against
     the page and this is a thing the eye is meant to be able to find on it. */
  --scroll-thumb: #3a3a41;
  --radius: 10px;
  /* The page's own margin, either side of everything in `main`. Named because
     one thing on the landing page is allowed out through it — the stage, on a
     narrow screen — and a bleed written as its own literal is a bleed that
     stops meeting the edge the first time this number moves. */
  --page-pad: 20px;
  /* How wide the rail is, and how much of the page it is taking. The same
     number until it collapses: then the rail keeps its width to slide out
     with, and the page gets that width back. */
  --rail: 264px;
  --rail-w: var(--rail);
  /* Where the app starts. The rail's first row and the name over the
     conversation are the tops of two columns either side of a border, and
     nothing in either layout would otherwise keep them on one line — so both
     take their distance from the top edge, and the height of that first row,
     from here. */
  --top-room: 22px;
  --top-row: 40px;
  /* How far the name's patch of background reaches down over the conversation,
     and the gap under it that the first message starts below: a bubble whose
     top edge is under the fade is washed out along that edge for as long as the
     chat sits unscrolled, which reads as a rendering fault rather than depth.
     One measurement, named once, used by both rules. */
  --top-fade: 16px;
  /* Which scheme the browser draws its own furniture in — the scrollbars below,
     the caret, a select's menu — and what an SVG loaded as an image resolves
     `prefers-color-scheme` against. It belongs with the palette it names, above
     the light block that overrides it. */
  color-scheme: dark;
}

:root.rail-hidden { --rail-w: 0px; }

/* The same palette against paper. The accent is the one token that does invert
   outright — with no hue in it there is nothing to preserve but the distance
   from the page, so it goes to the bottom of the range here as it went to the
   top there. The three verdict colours are the ones that cannot: they darken
   rather than flip, since a hue that glows on black is washed out on white. */
@media (prefers-color-scheme: light) {
  :root {
    --bg: #fafafa;
    --panel: #ffffff;
    --panel-2: #f1f1f2;
    --line: #e2e2e5;
    --text: #101012;
    --muted: #55555d;
    --accent: #101012;
    --accent-soft: rgba(16, 16, 18, 0.28);
    --on-accent: #ffffff;
    --robust: #2f8f57;
    --partial: #9a7415;
    --disputed: #c33a48;
    /* Paper, and whiter than the page is: --bg here is #fafafa, so a stage
       filled with the true white of the ink's opposite is the same gesture as
       the flat black above — a piece of the sheet with nothing drawn on it. */
    --stage-face: #ffffff;
    --scroll-thumb: #c2c2c8;
    color-scheme: light;
  }
}

* { box-sizing: border-box; }

/* --- scrollbars ------------------------------------------------------------ */

/* A scrollbar here is a report of where you are rather than a control —
   nothing in this app is got to by dragging one — so it is given the weight of
   a rule rather than of a bar, and the arrow buttons at its ends go with the
   width. The track is left transparent, so whatever the bar runs down shows
   through it: the page, the rail's panel, a block of code. Only the thumb is
   drawn.

   The colour is set once on the root and inherits, so the list of chats, a wide
   table, a block of code and the message field take it from here without being
   named. The width does not inherit and has to be said to everything, or the
   rail keeps a full-width bar with arrows on its ends while the page beside it
   has a thin one.

   `color-scheme` does the rest of the work and is declared with the dark
   palette at the top of this file rather than here — put here it would win over
   the light block above by being later in the file, and tell a paper page it
   was a dark one. */
:root {
  scrollbar-color: var(--scroll-thumb) transparent;
}

* { scrollbar-width: thin; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  display: flex;
  flex-direction: column;
  /* dvh so the sticky composer sits above a phone's toolbar rather than behind
     it; vh first for browsers that don't know the unit. */
  min-height: 100vh;
  min-height: 100dvh;
}

/* --- header --------------------------------------------------------------- */

.site-header {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 18px 24px;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  /* The narrow-screen settings sheet hangs off this. */
  position: relative;
}

/* The mark and the name are one lockup and are set as one: inline-flex, so the
   two sit on a shared middle line however big the name is set, and so the mark
   can be sized in the name's own em. Neither appears without the other. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.42em;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* The mark keeps the rounded tile it is drawn with, so it reads as the app's
   icon here and not as a picture of a ring. Slightly taller than the letters
   it stands beside: a square next to text has to overshoot the x-height to
   look the same size as it. */
.brand-mark { width: 1.45em; height: 1.45em; display: block; flex: none; }
/* The drawing fills the box the line has made room for, rather than coming out
   at the size its own attributes ask for — those are there for the browser
   that loads this file on its own as the favicon, and are 51 wide. */
.brand-mark svg { width: 100%; height: 100%; display: block; }

/* A link, but not one that announces itself as one: underlining the name would
   make the header look like a sentence with a word missing. */
a.brand { color: inherit; text-decoration: none; }
a.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }
.site-tag { color: var(--muted); font-size: 14px; }

/* The missing "u", put back but knocked off its baseline and set a shade below
   its neighbours. It never moves: the tilt is the whole joke, and a letter that
   shakes or answers the pointer is motion in the corner of the eye, a thing to
   be endured rather than read. */
.brand-u {
  display: inline-block;
  color: var(--muted);
  transform: translateY(-0.14em) rotate(-11deg);
}

/* Setup hangs off the composer at every width, as a popover above the button
   that opens it: three fields that lock after the first message are worth a
   press and not a permanent row of the header. */
.settings {
  position: absolute;
  left: 0;
  bottom: calc(100% + 10px);
  z-index: 40;
  display: none;
  width: min(300px, calc(100vw - 32px));
  padding: 14px;
  font-size: 14px;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.32);
}

.settings.open { display: grid; gap: 12px; }
.settings.locked { opacity: 0.6; }
.settings label { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
.settings label.check { justify-content: flex-start; gap: 5px; }
.settings select { max-width: 62%; }
.settings-locked { font-style: italic; }

.sheet-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Every input but the checkbox, rather than a list of the types in use: an
   unstyled field renders as a white box in a dark panel. */
select, textarea, input:not([type="checkbox"]) {
  font: inherit;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 5px 8px;
}

input[type="number"] { width: 58px; }

/* A button with nothing said about it is the page asking for something, and
   those are the ones that wear the accent: send, login, save. Every quieter
   button below states its own background and so keeps it. */
button {
  font: inherit;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 7px 14px;
  background: var(--accent);
  color: var(--on-accent);
}

button:disabled { opacity: 0.5; cursor: default; }

button.ghost {
  background: transparent;
  color: var(--muted);
  border-color: var(--line);
  padding: 5px 10px;
  font-size: 13px;
}

button.link {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  text-decoration: underline;
  font-size: inherit;
}

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

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 36px, because a thumb is not a cursor: the icon inside is 20. */
  width: 36px;
  height: 36px;
  padding: 0;
  background: transparent;
  color: var(--muted);
  border: 1px solid transparent;
  border-radius: 9px;
}

.icon-btn:hover, .icon-btn[aria-expanded="true"] {
  color: var(--text);
  background: var(--panel-2);
}

.icon-btn svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- layout --------------------------------------------------------------- */

/* The pages with a bar across the top: a column of reading width, centred. */
main {
  flex: 1;
  width: 100%;
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 var(--page-pad) 24px;
  display: flex;
  flex-direction: column;
}

/* The chat has no bar and no page margins of its own: the rail is flush to the
   left edge of the window, and the conversation is centred in whatever is
   left. */
body.app main { max-width: none; padding: 0; }

/* The rail is fixed rather than a column of this row, so a long chat scrolls
   under it instead of dragging it up the screen — and the padding that keeps
   the conversation clear of it is the rail's own width, which is zero once it
   is collapsed. One number, so the two can't disagree. */
.workspace {
  flex: 1;
  display: flex;
  padding-left: var(--rail-w);
  transition: padding-left 0.22s ease;
}

.pane {
  flex: 1;
  min-width: 0;
  max-width: 820px;
  margin: 0 auto;
  /* Nothing along the bottom edge. The room under the composer is the
     composer's own padding instead — see the note there — because a sticky box
     may not descend past the content box of the column it is in, and any
     padding left down here is a floor it stops on partway through the last of
     the scroll. */
  padding: 0 20px;
  display: flex;
  flex-direction: column;
  /* The pane is as tall as the window, so a short chat still puts its composer
     at the foot of the screen. Said by stretching into the row rather than by
     `min-height: 100%`, which is a question with no answer — the row is as tall
     as the pane, and the pane wants to be as tall as the row — and which the
     engines break differently: one drops back to the pane's own height, the
     other keeps the height it had last time and never lets it shrink again. */
  align-self: stretch;
}

/* The line above the conversation. Sticky, because the name of the thing being
   looked at — and the way back to the chats, when it is up here — has to
   survive being halfway down a long conversation. */
.pane-top {
  display: flex;
  align-items: center;
  justify-content: center;
  position: sticky;
  top: 0;
  z-index: 30;
  /* The rail's first row, restated on this side of the border: the same drop
     from the top edge and the same height, so the name and the rail's one
     action sit on one line across the whole app. */
  padding: var(--top-room) 0 6px;
  min-height: calc(var(--top-room) + var(--top-row) + 6px);
  background: var(--bg);
}

/* The name is the one thing here that is never put away, so it is the one thing
   that is never in the rail: it says itself once, in the middle of the
   conversation, whether the rail is open or shut. Set quietly, since it sits on
   top of the reading rather than beside it. */
.pane-top .brand { font-size: 17px; }

/* A corner each, and the window's corners rather than the conversation's: the
   pane is a centred column with room either side of it, and a button set
   against that column's edge sits in the middle of nowhere. Fixed, on a patch
   of the page's own colour, so they hold the corner and whatever scrolls past
   goes under them. Neither is in the row's flow, so the name is centred on the
   conversation and not on what is left of it.

   Left calls the rail back, and is only up while the rail is away — while it is
   open, the rail's own copy of the button is holding that corner. Right starts
   a chat, which is done to the conversation rather than to the rail, so it is
   up whatever the rail is doing. */
.pane-tools, .pane-actions {
  display: flex;
  position: fixed;
  top: 0;
  z-index: 40;
  align-items: center;
  /* Off the row but still on its line: the same drop from the top edge and the
     same row height as the rail's first button and the name, so the corners
     stay level with a name they are nowhere near. */
  padding: var(--top-room) 10px 0;
  min-height: calc(var(--top-room) + var(--top-row));
  background: var(--bg);
}

.pane-tools {
  left: 0;
  display: none;
  border-bottom-right-radius: var(--radius);
}

.pane-actions {
  right: 0;
  border-bottom-left-radius: var(--radius);
}

:root.rail-hidden .pane-tools { display: flex; }

/* No rule across the page, but nothing scrolling under the name arrives at it
   whole: whatever is going past is already half gone by the time it reaches
   the edge, and a line of a message is never cut through its own letters. */
.pane-top::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: var(--top-fade);
  background: linear-gradient(var(--bg), transparent);
  pointer-events: none;
}

/* Clear of the fade, plus the gap that makes it a gap. */
.chat { flex: 1; padding-top: calc(var(--top-fade) + 8px); }

/* A chat with nothing in it is not a conversation that happens to be empty at
   the top: it is one field with the name over it, and both belong where the eye
   lands rather than pinned to the two far edges of the screen.

   The pair is centred by giving the conversation an auto margin above and the
   field an auto margin below — a flex column hands its free space to those two
   in equal shares, so nothing has to be measured. The conversation stops
   claiming the space first (`flex: 0 0 auto`) or there would be none left to
   share, and the field stops being sticky, which is a rule about where to stop
   when scrolled and there is nothing here to scroll. */
:root.chat-blank .chat {
  flex: 0 0 auto;
  margin-top: auto;
  padding-top: 0;
  text-align: center;
}

/* The name, said once. It is the same lockup either way, so which copy shows is
   the only thing that changes with the layout. */
:root.chat-blank .pane-top .brand { display: none; }
.empty .brand { font-size: 26px; }

/* The gap over the field, and the reason the whole group sits above the middle
   rather than on it. The auto margins split what is left evenly and cannot be
   weighted, so the lift is bought with padding under the field: room added to
   the bottom of the group is half of it taken off the top, and the name ends up
   nearer eye level than dead centre — which is where it is read from and where
   a page's optical centre has always been. */
:root.chat-blank .composer {
  position: static;
  margin-bottom: auto;
  padding: 48px 0 76px;
  background: none;
}

/* The centred field is a first question, not the foot of a column of them: it
   reads as one object with the line above it rather than as the full width of
   a conversation that has not started. */
:root.chat-blank .composer-box { max-width: 660px; margin: 0 auto; }

.muted { color: var(--muted); }
.error { color: var(--disputed); }

/* --- turns ---------------------------------------------------------------- */

/* minmax(0, …) rather than the 1fr a grid gives a row by default: a row's
   default floor is the widest thing inside it, and an answer can hold a table
   or a line of code wider than the screen. Left at that floor the turn grows to
   fit it and takes the page sideways with it, instead of letting the block that
   is too wide scroll inside its own edges. */
.turns {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 22px;
}

.bubble-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }

.bubble.user {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-left: auto;
  max-width: 85%;
  width: fit-content;
}

.bubble.assistant { padding: 14px 0 0; }

/* The cursor goes on the last words written rather than after everything, or a
   compiled answer would hold it on a line of its own under the last paragraph —
   or under the last list, a bullet's worth to the left of the text it belongs
   to. So it hangs off the deepest last thing on the page: the end of the chain
   of last children, down to whichever of them holds text. Before the first chunk
   there is no chain to follow, and the empty container carries it instead. */
.streaming > :last-child:not(:has(*))::after,
.streaming > :last-child > :last-child:not(:has(*))::after,
.streaming > :last-child > :last-child > :last-child:not(:has(*))::after,
.streaming > :last-child > :last-child > :last-child > :last-child:not(:has(*))::after,
.streaming:empty::after {
  content: "▍";
  color: var(--accent);
  animation: blink 1s steps(2) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

/* --- a compiled answer ----------------------------------------------------- */

/* The blocks markdown.js builds. An answer sets its own spacing from here on,
   so the container stops holding the line breaks itself. */
.markdown { white-space: normal; }
.markdown > :first-child { margin-top: 0; }
.markdown > :last-child { margin-bottom: 0; }
.markdown p { margin: 0 0 12px; }

/* An answer's headings are landmarks inside one message, not the page's own, so
   they step down gently and stay near the size of the text around them. */
.markdown h1, .markdown h2, .markdown h3,
.markdown h4, .markdown h5, .markdown h6 {
  margin: 20px 0 8px;
  line-height: 1.3;
  font-weight: 650;
}

.markdown h1 { font-size: 20px; }
.markdown h2 { font-size: 18px; }
.markdown h3 { font-size: 16px; }
.markdown h4, .markdown h5, .markdown h6 { font-size: 15px; color: var(--muted); }

/* The markers are named rather than left to the browser: an answer's list sits
   inside the list of turns, and a browser counting depth from there starts a
   first-level list on the marker it keeps for second-level ones. */
.markdown ul, .markdown ol { margin: 0 0 12px; padding-left: 22px; }
.markdown ul { list-style: disc; }
.markdown ul ul { list-style: circle; }
.markdown ul ul ul { list-style: square; }
.markdown ol { list-style: decimal; }
.markdown li { margin: 3px 0; }
/* A one-line item is a paragraph too, and reads as a line of the list. */
.markdown li > p { margin: 0 0 4px; }
.markdown li > :last-child { margin-bottom: 0; }
.markdown li > ul, .markdown li > ol { margin-top: 4px; }

.markdown a { color: var(--accent); }

.markdown code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.9em;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 4px;
}

/* A block of code keeps its own line breaks and scrolls rather than wrapping:
   wrapped code reads as code with a bug in it. */
.markdown pre {
  margin: 0 0 12px;
  padding: 11px 13px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow-x: auto;
}

.markdown pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: 13px;
  white-space: pre;
}

.markdown blockquote {
  margin: 0 0 12px;
  padding-left: 12px;
  border-left: 3px solid var(--line);
  color: var(--muted);
}

.markdown hr { border: 0; border-top: 1px solid var(--line); margin: 18px 0; }

/* The table scrolls inside its own box: a wide one must not push the chat
   sideways on a phone. */
.table-scroll { overflow-x: auto; margin: 0 0 12px; }

.markdown table { border-collapse: collapse; font-size: 14px; }
.markdown th, .markdown td {
  border: 1px solid var(--line);
  padding: 5px 10px;
  text-align: left;
  vertical-align: top;
}
.markdown th { background: var(--panel-2); font-weight: 600; }

.markdown img { max-width: 100%; }

/* --- compiled maths -------------------------------------------------------- */

/* The shapes math.js builds. There is no maths font here to fall back on and no
   font shipped, so the stack asks for the ones a system is likely to already
   have and settles for a serif: against the interface's sans, a serif italic is
   already read as a variable. */
.math {
  font-family: "Latin Modern Math", "STIX Two Math", "Cambria Math", "Times New Roman", serif;
  font-size: 1.08em;
  line-height: 1.2;
  white-space: nowrap;
}

/* A formula on its own line is centred and given room, and scrolls inside its
   own box rather than pushing the chat sideways on a phone — the same bargain
   the tables make. */
.math-scroll { overflow-x: auto; margin: 0 0 12px; text-align: center; }
.math-display { display: inline-block; font-size: 1.18em; padding: 4px 0; }

/* What could not be parsed, kept as the source it came in as. */
.math-raw, .m-unknown {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.85em;
  color: var(--muted);
}

/* A variable leans, a number stands up, a named function is a word. */
.m-var { font-style: italic; padding-right: 0.02em; }
.m-name, .m-mathrm, .m-text { font-style: normal; }
.m-name { padding: 0 0.12em 0 0.05em; }
.m-mathbf { font-weight: 700; }
.m-mathit { font-style: italic; }
.m-mathsf { font-family: inherit; font-style: normal; }
.m-mathtt { font-family: ui-monospace, Menlo, Consolas, monospace; }
.m-bold { font-weight: 700; }
.m-italic { font-style: italic; }
.m-text { font-family: inherit; font-size: 0.92em; padding: 0 0.15em; }

/* Spacing is the grammar: a relation gets room either side so the two halves of
   an equation read as two halves, an operator rather less, an ordinary symbol
   none at all. */
.m-rel { margin: 0 0.25em; }
.m-bin { margin: 0 0.18em; }
.m-punct { margin-right: 0.16em; }
.m-open { margin-left: 0.05em; }
.m-close { margin-right: 0.05em; }
.m-space { display: inline-block; }
.m-prime { font-size: 1.1em; vertical-align: 0.06em; }

.m-frac {
  display: inline-flex;
  flex-direction: column;
  vertical-align: middle;
  text-align: center;
  font-size: 0.95em;
  margin: 0 0.12em;
}
.m-num { padding: 0 0.3em 0.08em; border-bottom: 1px solid currentColor; }
.m-den { padding: 0.08em 0.3em 0; }
/* A binomial is the same stack without the rule between. */
.m-open-frac .m-num { border-bottom: 0; }
/* In a line of prose a full-size fraction sets the leading for the whole
   paragraph, and one $\frac{a}{b}$ in a sentence opens a gap above and below
   every line of it. Down here it stays a fraction and the paragraph stays a
   paragraph; a formula that wants its full height can have a line of its own. */
.math:not(.math-display) .m-frac { font-size: 0.82em; }

/* Scripts, stacked in a column so that x_i^2 puts one over the other rather
   than one after the other. */
.m-scripts {
  display: inline-flex;
  flex-direction: column;
  vertical-align: middle;
  font-size: 0.72em;
  line-height: 1.05;
  text-align: left;
  padding-left: 0.06em;
}
.m-sup { margin-bottom: 0.25em; }
.m-sub { margin-top: 0.25em; }
/* One script alone still sits where a script sits, not on the middle of the
   line: the column has nothing to balance it against, so it is nudged. */
.m-scripts > .m-sup:only-child { margin: -0.55em 0 0.55em; }
.m-scripts > .m-sub:only-child { margin: 0.55em 0 -0.55em; }

.m-big { font-size: 1.5em; line-height: 1; vertical-align: -0.16em; }

/* A sum's limits, above and below where a display has the height for it. */
.m-limits {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  vertical-align: middle;
  margin: 0 0.15em;
}
.m-over, .m-under { font-size: 0.68em; line-height: 1.1; }
.m-over { margin-bottom: 0.1em; }
.m-under { margin-top: 0.1em; }

/* The radical: a surd stretched to its radicand, and the bar over the radicand
   drawn as that radicand's own top border, so the two always meet. The pair is
   laid out on their bottom edges rather than on a shared baseline — a baseline
   is where the radicand's text sits, and what has to line up here is the top of
   the glyph with the left end of the bar. */
/* The radicand sets the height and the surd is painted down the side of it,
   out of the flow: an <svg> with a width and a viewBox has an aspect ratio, and
   left in the flow it would report a height from that ratio and make the box as
   tall as the surd is thin. Positioned, it has no say in the height and simply
   fills whatever the radicand turned out to be. */
.m-sqrt {
  display: inline-block;
  position: relative;
  padding-left: 0.6em;
  vertical-align: middle;
  white-space: nowrap;
}
.m-surd { position: absolute; left: 0; top: 0; width: 0.6em; height: 100%; }
.m-rad {
  display: block;
  border-top: 1px solid currentColor;
  padding: 0.12em 0.25em 0 0.1em;
}
/* The index of a root sits over the foot of the surd and takes no room doing
   it, which is where TeX puts it and why \sqrt[3]{8} is as wide as \sqrt{8}. */
.m-index {
  position: absolute;
  left: 0;
  top: -0.3em;
  font-size: 0.6em;
}

/* Brackets around something tall. The scale is worked out in math.js from the
   height it counted as it built the row — nothing on this page has been laid
   out yet at the moment the answer is drawn. */
.m-fence {
  display: inline-block;
  transform-origin: center 45%;
  padding: 0 0.02em;
}

/* An accent hangs over what it marks and takes no room doing it, so a hatted
   letter is as wide and as tall as the letter. `line-height` is pulled in
   because the mark is placed off the top of this box, and the box would
   otherwise be the answer's leading rather than the letter. */
.m-accented { display: inline-block; position: relative; line-height: 1; text-align: center; }
.m-accent {
  position: absolute;
  left: 0;
  right: 0;
  top: -0.46em;
  font-size: 0.8em;
  line-height: 1;
}
.m-accent-wide { font-size: 0.62em; top: -0.5em; }

.m-over-rule { border-top: 1px solid currentColor; padding: 0.1em 0.1em 0; }
.m-under-rule { border-bottom: 1px solid currentColor; padding: 0 0.1em 0.1em; }
.m-over-brace, .m-under-brace { padding: 0 0.1em; }
.m-over-brace { border-top: 1px solid currentColor; border-radius: 40% 40% 0 0 / 0.5em 0.5em 0 0; }
.m-under-brace { border-bottom: 1px solid currentColor; border-radius: 0 0 40% 40% / 0 0 0.5em 0.5em; }

.m-boxed {
  display: inline-block;
  border: 1px solid currentColor;
  border-radius: 3px;
  padding: 0.15em 0.35em;
}

/* Matrices, cases and aligned, which are one grid wearing three hats. */
.m-grid { display: inline-table; vertical-align: middle; margin: 0 0.1em; }
.m-row { display: table-row; }
.m-cell { display: table-cell; padding: 0.16em 0.4em; }
.m-centre .m-cell { text-align: center; }
.m-left .m-cell { text-align: left; }
/* Aligned leans its columns towards each other, so the equals signs of a
   worked derivation stand in one line down the page. The two lean together with
   nothing between them: the & of "&=" is a seam in an expression, not a gap. */
.m-lean .m-cell { text-align: left; padding-left: 0; }
.m-lean .m-cell:first-child { text-align: right; padding-right: 0; }

.m-substack {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  font-size: 0.9em;
  line-height: 1.05;
}

.answer-footer { margin-top: 12px; }

.validate-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* Quiet until used: an empty axes box sits under every answer, and the run it
   belongs to is fine without it. `.option` is the same box for whatever the
   next strategy asks for, since a strategy's controls are drawn from what it
   declared rather than written into this file. */
input.axes,
input.option {
  width: 270px;
  max-width: 100%;
  font-size: 13px;
  padding: 5px 9px;
  background: transparent;
}

input.axes:focus,
input.option:focus { background: var(--panel-2); }

/* One strategy's controls, side by side. A group of its own so the whole set
   can be swapped when a different strategy is chosen, and so the narrow-screen
   fold below has one thing to fold. */
.validate-fields { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.option-check { font-size: 13px; color: var(--muted); }

/* Which way to validate. Hidden while there is one way — a menu of one is a
   question with no answer to give. */
select.strategy { font-size: 13px; padding: 4px 8px; }

button.validate {
  background: transparent;
  color: var(--muted);
  border-color: var(--line);
  font-size: 13px;
  padding: 5px 11px;
}

button.validate:hover:not(:disabled) { color: var(--text); border-color: var(--accent); }

/* On a phone the axes box is a second line under every answer, spent on a
   field most runs leave empty. It folds behind this, which costs one line of
   nothing instead. A wide screen has room for the box itself and never shows
   the button. */
button.axes-toggle { display: none; }

/* --- several verdicts on one answer ----------------------------------------- */

/* An answer reworded and critiqued has two verdicts, and the second measures
   something the first did not — so they sit side by side and this strip picks
   between them. Hidden while there is only one. */
.verdict-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }

.verdict-tab {
  font-size: 12px;
  padding: 4px 11px;
  border-radius: 999px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--muted);
}

.verdict-tab:hover { color: var(--text); }
.verdict-tab.current { color: var(--text); border-color: var(--accent); background: var(--panel-2); }

/* --- validation panel ------------------------------------------------------ */

.validation {
  margin-top: 12px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
}

.validation.pending { color: var(--muted); font-size: 13px; font-style: italic; }
/* Marked by the status a reader is looking for, whatever the strategy calls it:
   the verdict names which of its statuses that is (`tone: "alarm"`), which for
   the rephrase sweep is its disputed claims. */
.validation.has-alarm { border-left-color: var(--disputed); }

/* --- a validation in progress ---------------------------------------------- */

.progress-head { display: flex; align-items: baseline; gap: 10px; }

.progress-label { margin: 0; flex: 1; }

/* Which phase of how many, in figures, for the part of a wait where the wording
   has stopped being news. */
.progress-count {
  font-style: normal;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  opacity: 0.7;
}

.progress { display: flex; gap: 5px; margin-top: 10px; }

.progress-step {
  position: relative;
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}

/* Behind, and done with. Mixed rather than taken at alpha, which on a bar 4px
   tall would leave a finished phase only a shade above the one still to come:
   most of the accent instead, so how far along a run is reads at a glance. The
   phase under way stays darker than this and says so by moving. */
.progress-step.done { background: color-mix(in srgb, var(--accent) 72%, var(--line)); }

/* The phase under way. A sweep rather than a fraction of one: the server says
   which phase it is in and never how far through it, so the bar counts phases
   and this one says "working" instead of inventing a number.

   The tint under the sweep is what makes it the phase it is even at the moment
   the sweep is off the end of it; without it the running segment is blank for
   part of every cycle and the bar reads as one phase behind. */
.progress-step.running {
  background: color-mix(in srgb, var(--accent) 22%, var(--line));
}

.progress-step.running::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: sweep 1.4s ease-in-out infinite;
}

@keyframes sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}

/* Waiting to rejoin: the phase it stopped in is still the phase it is in, so
   the bar holds its place and only stops moving. */
.progress.stalled .progress-step.running::after { animation-play-state: paused; opacity: 0.4; }

/* Without the sweep the running phase still has to be tellable from the ones
   ahead of it, so it takes the accent outright. */
@media (prefers-reduced-motion: reduce) {
  .progress-step.running::after {
    animation: none;
    transform: none;
    background: var(--accent);
    opacity: 0.7;
  }
}

.validation-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 10px;
}

/* Collapsed, the counts row is the whole panel. */
.validation.collapsed .validation-head { margin-bottom: 0; }

button.collapse { margin-left: auto; font-size: 13px; }

/* The counts take the width that is left and wrap within it, so a narrow panel
   spends a line on a second row of pills rather than on the toggle alone. */
.pills { list-style: none; flex: 1; display: flex; flex-wrap: wrap; gap: 8px; padding: 0; margin: 0; }

.pill {
  font-size: 13px;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
}

.pill b { margin-right: 4px; }
.pill.zero { opacity: 0.45; }

/* Coloured by the tone the strategy gave the status rather than by its name, so
   a strategy whose statuses this file has never heard of still arrives in the
   colours the app reads in. */
.pill.tone-good b { color: var(--robust); }
.pill.tone-warn b { color: var(--partial); }
.pill.tone-alarm b { color: var(--disputed); }
.pill.tone-neutral b { color: var(--muted); }

.validation-headline { margin: 0 0 6px; font-weight: 600; }
.validation-summary { margin: 0 0 14px; color: var(--muted); }

.chip {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 7px;
  border-radius: 5px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  white-space: nowrap;
}

.chip.tone-good { color: var(--robust); }
.chip.tone-warn { color: var(--partial); }
.chip.tone-alarm { color: var(--disputed); }
.chip.tone-neutral { color: var(--muted); }

.findings { list-style: none; padding: 0; margin: 0; display: grid; gap: 10px; }

.finding {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 10px;
  align-items: start;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.finding-text { margin: 0; }
.finding-src { margin: 3px 0 0; font-size: 12px; color: var(--muted); }
.finding-count { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.findings-note { font-size: 13px; color: var(--muted); margin: 12px 0 0; }

.aside { margin-top: 14px; font-size: 13px; }
.aside summary { cursor: pointer; color: var(--muted); }

/* minmax(0, …) for the same reason the turns have it: these are answers too,
   and one of them can hold a block wider than the panel. */
.rewordings {
  list-style: none;
  padding: 0;
  margin: 10px 0 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
}
/* The rule marks one rewording, so it is the direct children that get it: an
   answer written as a list would otherwise draw the same rule down every item
   of it. */
.rewordings > li { border-left: 2px solid var(--line); padding-left: 12px; }

.axis {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--accent);
}

.reworded { margin: 4px 0 6px; }
.reworded-answer { margin: 0; color: var(--muted); }

.meta { margin: 14px 0 0; font-size: 12px; color: var(--muted); }

/* What the run cost, and the way out of it, on one line: the end of the panel
   is one row rather than two, and the fold sits at the far edge where the
   head's own is. They wrap apart on a screen too narrow for both. */
.validation-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 16px;
}

.validation-foot .meta { flex: 1; }

button.collapse-foot { font-size: 12px; white-space: nowrap; }

/* Folding from the foot brings the panel back into view, and a little of the
   answer above it, so the counts land under something rather than against the
   top edge. */
.validation { scroll-margin-top: 16px; }

/* --- composer -------------------------------------------------------------- */

/* The room under the field belongs to the field and not to the column around
   it. A sticky box is held inside the content box of its parent, so padding
   along the bottom of the pane would be a floor the composer stops on partway
   through the last of the scroll — walking up the screen and opening a strip of
   nothing under it. Carried here instead, the box reaches the true bottom of
   the column and the gap under the field is drawn rather than reserved. */
.composer {
  position: sticky;
  bottom: 0;
  padding: 14px 0 24px;
  background: linear-gradient(to top, var(--bg) 70%, transparent);
}

/* The field and the two controls that act on it are one box, on one line:
   setup, the message, send. The box is what the sheet is positioned against,
   so it is the relative one.

   Ends rather than centres, so that a message grown to several lines leaves
   the buttons on its last one — level with the line being typed, which is
   where the eye already is, instead of drifting to the middle of a block of
   text. At one line, which is most of them, the two are the same thing. */
.composer-box {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding: 8px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 28px;
}

/* Nothing marks focus here. This is the one field on the page, it is focused
   from the moment the page loads, and it is the thing a person came to type in,
   so a border that lit up would announce a state that was never in doubt and
   flash on every click into a box the caret already sat in. The caret is the
   indicator. (The setup sheet's own fields keep theirs — those are several
   controls in a row, where which one has the keyboard is a real question.) */

/* Past one line the row stops being a row. Two buttons level with the last line
   of a block of text are two buttons hanging off the end of it, and the text
   has been paying for their width the whole way up — so the message takes the
   top of the box on its own and the controls drop to a line under it, which is
   where the field wins back the 80px it was renting.

   Which state to be in is decided at the narrow width every time (see
   fitMessage), never at the width the box is currently wearing: text that wraps
   at the compact width and does not wrap once it is wide would otherwise
   unwrap, unstack, wrap again, and shudder on every keystroke. */
.composer-box.stacked { flex-wrap: wrap; }
.composer-box.stacked textarea { order: -1; flex-basis: 100%; padding: 7px 12px 3px; }

/* The textarea is not a box of its own — no border, no background, no rounding
   — so that the box around it can be. Its padding is what puts one line of text
   at the height of the buttons beside it. */
.composer textarea {
  flex: 1;
  min-width: 0;
  resize: none;
  max-height: 220px;
  /* Set larger than the rest of the page, which nothing else here is. The
     message is the only thing on this screen a person writes rather than reads,
     and it is read back at this size while it is being written. */
  font-size: 17px;
  padding: 10px 12px;
  /* Stated in pixels because the script divides by it: a line count read off a
     `normal` line-height is a line count read off whatever the font felt like. */
  line-height: 26px;
  border: 0;
  border-radius: 0;
  background: none;
  outline: none;
}

/* Setup, quiet, on the left: the same round button as send, wearing the border
   instead of the accent, because it is the one of the two you do not press. */
.composer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  background: transparent;
  color: var(--muted);
  border-color: var(--line);
  border-radius: 50%;
}

.composer-btn:hover, .composer-btn[aria-expanded="true"] {
  color: var(--text);
  background: var(--panel);
}

.composer-btn svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Send, on the right: a round button the size of the row, holding an arrow
   rather than the word. */
.send-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  width: 42px;
  height: 42px;
  padding: 0;
  border-radius: 50%;
}

.send-btn svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* An empty box is not a failure, so the button says there is nothing to send by
   going quiet rather than by staying lit and refusing. Full opacity, unlike
   every other disabled button, since this replaces the accent rather than
   washing it out. Addressed by id so the demo's send on the landing page keeps
   the ordinary treatment — that one is disabled because nothing on the stage is
   pressable, not because the box is empty. */
#send:disabled {
  opacity: 1;
  background: var(--panel);
  color: var(--muted);
  border-color: var(--line);
}

/* --- login ----------------------------------------------------------------- */

/* The width is stated rather than left to `auto`: beside the auto margins,
   `auto` sizes the panel to the passcode box's own 20 characters — wider than a
   phone — and a max-width alone does not bring it back under. */
.panel.login {
  margin: 60px auto;
  width: 100%;
  max-width: 400px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
}

/* Logging in is the one of these that a stranger reaches, so it stands on the
   same water as the page that sent them there: translucent, so the form is a
   pane laid on the drawing rather than a hole cut in it — the treatment the
   demo's frame gets on the landing page.

   And pushed down far enough to land near the middle of the screen: there is no
   bar over this page, so without it the form sits under the mark with the whole
   rest of the window empty below. Said in a margin rather than by centring the
   column, which would drag the name down out of the corner it belongs in. */
body.drawn .panel.login {
  margin-top: clamp(24px, 24vh, 240px);
  background: color-mix(in srgb, var(--panel) 78%, transparent);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
}

/* Set like the line that closes the landing page, because this is the same
   voice one press further on. */
.panel.login h1 {
  margin: 0 0 18px;
  font-size: 24px;
  letter-spacing: -0.02em;
}
.panel.login form { display: flex; gap: 10px; }
.panel.login input { padding: 9px 11px; }

/* One row for the single passcode box, a column for the account form. `flex`
   belongs only to the row: down a column it stretches each field to fill the
   height instead of leaving them their own. */
.panel.login input { min-width: 0; }
.panel.login form:not(.stack) input { flex: 1; }
.panel.login form.stack { flex-direction: column; align-items: stretch; }
.panel.login form.stack button { align-self: flex-start; margin-top: 2px; }

.small { font-size: 13px; }
.panel.login .error { margin-bottom: 0; }

/* The way to the other form, at the foot of each. Set quiet: whoever needs it
   is looking for it, and whoever does not should read past it to the button.
   The link is coloured because these pages have no global rule for one, and a
   browser's default blue is the single loudest thing on either of them. */
.panel.login .other-door { margin: 18px 0 0; }
/* Two ways out under one form — a reset and a signup — are one group, not two
   afterthoughts, so the second follows the first at reading distance rather
   than at the distance either sits from the form. */
.panel.login .other-door + .other-door { margin-top: 6px; }
.panel.login .other-door a {
  color: var(--text);
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 2px;
}
.panel.login .other-door a:hover { text-decoration-color: var(--accent); }

/* --- the drawn pages ---------------------------------------------------------- */

/* The landing page and the way in, which are the two a stranger sees: both are
   a drawing with something standing on it, and both say so with `drawn` on the
   body. What follows is what they share — the water, the mark on it, and the
   one quiet link in the corner.

   Two things have to be settled before anything else: the drawing is behind
   everything and takes no pointer, and whatever stands on it is above. A
   positioned canvas paints over an unpositioned column however early in the
   document it comes — so the column is positioned too, and given the higher
   layer. */
body.drawn { position: relative; }
body.drawn main { position: relative; z-index: 1; }

/* The hero's pool of calm reaches past its own box, and on a narrow screen past
   the screen with it. Clipped here rather than narrowed there, because the
   reach is the effect: the gradient is already transparent by the time it gets
   to either end, so cutting it at the edge of the page takes nothing off it —
   it simply runs to both edges the way it already runs off the left one, which
   nobody could scroll to either.

   `clip` and not `hidden`: hidden would make this a scroll container, and a
   scroll container is a new containing block and a new scrollport for anything
   fixed or sticky inside it. Only the chat has either, and only the chat is
   spared this rule — but the two are one line apart and would not stay that
   way. Nothing here is clipped vertically: with `clip` the other axis is
   allowed to stay visible, which `hidden` would not have permitted. */
body.drawn main { overflow-x: clip; }

/* The ocean, drawn by static/landing-art.js into a canvas that is fixed rather
   than scrolled: it is the surface the whole page floats on rather than a
   picture at the top of it. Nothing on it that a reader could be waiting to be
   told, which is what lets it be hidden outright rather than described. */
.art {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 0;
  width: 100%;
  /* The large viewport, not the current one. A phone hides its address bar as
     you scroll down and brings it back as you scroll up, and on those two
     moments `100%` is a different number than it was a frame earlier — so the
     water would grow and shrink under the page every time the reader changed
     direction. `lvh` is the height with the bar retracted and stays that
     number throughout, which costs a strip of drawing under the bar while it
     is out and buys a surface that does not move. The percentage stays as the
     fallback for anything that has never heard of the unit. */
  height: 100%;
  height: 100lvh;
  display: block;
  opacity: 0.85;
  pointer-events: none;
  /* A layer of its own, so a scroll is the page moving over the drawing rather
     than the drawing being rasterised again on every frame it is composited
     with. Free here, and on a phone it is the difference between a still
     surface and a shimmering one. */
  transform: translateZ(0);
}

/* The mark, the name, and the way on — standing on the water rather than sat on
   a bar. It scrolls away with everything else: anything pinned would need a
   ground of its own to be read against, and a ground over a full-page drawing
   is a rectangle of missing picture travelling down the screen. So the visitor
   arrives at the top, where the name is the first thing on the page, and leaves
   at the bottom, where the call to action is waiting instead.

   A sibling of what it sits over rather than a row inside it, so the 64px
   between the landing page's sections is not also the gap under the name; and
   it carries the column's width itself, since main is wider than either page. */
.page-brand {
  width: 100%;
  max-width: 820px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: max(22px, env(safe-area-inset-top)) 0 48px;
}

/* The controls in that row, as against the name at the left of it. What they
   look like is `.btn-quiet.small`, written once up there and worn here too —
   this class is only where in the row they sit. It goes on the first of them,
   and the row's own gap spaces whatever follows: the landing page logged out
   carries two, a way in and a way to make one. */
.corner-link { margin-left: auto; }

/* --- landing ----------------------------------------------------------------- */

/* Reading width rather than the 1140 the workspace needs: this page is prose
   and one chat-shaped stage, and neither is improved by being wider than the
   chat itself.

   minmax(0, 1fr) rather than the auto track a bare grid gives: an auto track is
   at least as wide as the widest thing in it asks to be, and the demo's box
   asks for a textarea's own 20 characters — wider than a small phone, which
   takes the whole page sideways under a thumb. */
.landing {
  width: 100%;
  max-width: 820px;
  margin: 0 auto;
  padding: 10px 0 96px;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 64px;
}

/* Words on open water. The drawing is what the page is, so it is not turned
   down across the whole screen to make room for them — the hero carries its own
   patch of calm instead, a soft pool of the page's own colour with no edge
   anywhere for the eye to catch on.

   closest-side rather than a size of its own: the ellipse then ends exactly on
   the box it is drawn in and has nowhere to be clipped, which is the one way a
   soft gradient acquires a hard straight edge.

   The pool is wider than the words on purpose, and on a phone that reach is
   wider than the screen: the hero is nearly the full width down there, so the
   170 either side lands past the right edge and the page could be dragged
   sideways to a strip of nothing. The reach stays — it is what keeps the pool
   from ending anywhere the eye can find — and `body.drawn main` below stops it
   from being somewhere to scroll to. */
.hero { position: relative; }
.hero::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: -70px -170px -50px -170px;
  background: radial-gradient(
    ellipse closest-side at 50% 50%,
    color-mix(in srgb, var(--bg) 92%, transparent) 0%,
    color-mix(in srgb, var(--bg) 68%, transparent) 52%,
    transparent 100%
  );
}

/* The tagline, as the smallest line on the page rather than as furniture over
   it. */
.eyebrow {
  margin: 0 0 18px;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The claim. Light rather than bold: eight words at this scale hold the ground
   without shouting across it, where bold would make a poster of them and
   compete with the demo underneath for the first look. The tracking opens a
   touch, since thin letters at this size need room bold ones do not.

   No bottom margin of its own — the line under it sets its own top gap, and the
   gap from the hero down to the demo belongs to the landing grid.

   54 is the ceiling rather than a taste: the longer half is "Some of the answer
   was about", which at 54 fills 803 of the 820 the page's column is wide, and
   one step further wraps it to three lines. Bigger would mean taking the hero
   out of the column the demo, the buckets and the closing all share.

   `max-width` is in `ch` and so scales with the font, which keeps the line
   breaking after "about" at every size in this range. */
.hero h1 {
  margin: 0;
  font-size: clamp(32px, 4.9vw, 54px);
  font-weight: 300;
  line-height: 1.16;
  letter-spacing: -0.02em;
  max-width: 26ch;
  text-wrap: balance;
}

/* The headline is one sentence in two halves, and each half takes a line of its
   own. The break is where the claim starts: a turn that begins halfway along a
   line reads as a continuation of what came before it, and this one is the
   point of the whole sentence.

   No leading between them: one sentence over two lines wants the line-height it
   already has and nothing more, or the halves read as unrelated.

   Both halves named, and the rule written to the names rather than to `span`.
   A bare descendant selector here would also catch whatever a later edit puts
   inside the headline — an abbreviation, a marked-up word — and turn it into a
   line of its own, which is a strange failure to have to find. */
.hero h1 .dim,
.hero h1 .turn {
  display: block;
}

/* The half that is scaffolding rather than claim, set back in the muted grey.
   Which half is dimmed is the whole of the decision, and it goes to the one
   whose only job is to get the sentence to "how you asked."

   Grey because there is nothing else: the accent here is achromatic by design,
   so the only emphasis on this page is which flat shade of the one range a
   thing is set in. */
.hero h1 .dim { color: var(--muted); }

/* What the app is, in one line, under the claim about what it feels like. Set
   at the weight of an aside rather than of a second headline, and muted, so it
   is available rather than insisted on.

   54ch and not the headline's 26: this is prose and is read a line at a time,
   where the headline is a shape and is taken whole. Top margin only — the room
   under the hero belongs to the landing grid. */
.hero-sub {
  margin: 20px 0 0;
  max-width: 54ch;
  color: var(--muted);
  font-size: 17px;
}

/* One frame, holding one thing at a time: first the question and the way to
   start it, then the run itself. The button that starts it is inside the frame
   it is about to fill — a play control loose on the page above the stage is a
   press whose consequence is somewhere else.

   The frame has no border, no rounding, no fill of its own and nothing blurred
   behind it — all four would make a component of it, and this is a page made of
   words, hairlines and a drawing. A half-transparent fill is the worst of them,
   since the light in the ocean pools exactly where the box sits and the frame
   would lay grey over the best stretch of the drawing.

   What holds the stage together is two drawn lines and what is between them:
   see _wave_over.html for the curve, and .poster / .demo-body below for the
   face. The sides stay open — the column edge is where this page ends
   everything else.

   So `.stagebox` has no rule of its own, and this note is here to say that is
   deliberate rather than lost. */

/* The two rules, in the muted grey held back from it: a drawn line has to be a
   shade above the ruled ones or it reads as a rendering fault rather than as a
   hand, and a shade below the text or the frame is louder than the run inside
   it. Stated as a colour rather than as `opacity` on the span, because each of
   these SVGs paints the face as well as the line — and a strip of face at 62%
   over a face at 100% is a band across the stage. */
.hand-rule {
  display: block;
  color: color-mix(in srgb, var(--muted) 62%, transparent);
}

.hand-rule svg { display: block; width: 100%; height: 12px; }
.wave-face { fill: var(--stage-face); }

/* Where the strip meets the block under it. An SVG's filled edge and a
   background's edge that land on the same fractional device pixel leave a
   hairline of water between them, which on a black stage is a bright thread
   across the page. A pixel of overlap and there is no seam to find. */
.hand-rule:first-child { margin-bottom: -1px; }
.hand-rule:last-child { margin-top: -1px; }

/* There is no caption over the stage, and nothing here draws one: what tells a
   visitor this is a recording is that the question types itself. */

/* The still frame, with the question already in it — because it is the question
   the run is about to type, so the play button is the only thing left to
   decide. A class's worth of `display` outranks the browser's own [hidden]
   rule, so the press that takes the frame away has to be spelled out. */
.poster[hidden] { display: none; }

/* One row, on the same left edge as the eyebrow, the headline and the sub
   above it: the question and the two presses share a line, and the frame comes
   down to the height of what is in it.

   The face is here rather than on .stagebox, and on .demo-body for the same
   reason: the two drawn rules fill their own half of their strip, and a
   background on the box behind them would run straight past the curve and put
   back the flat edge the curve is there to replace. */
.poster {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  justify-items: start;
  gap: 20px 28px;
  padding: 26px;
  background: var(--stage-face);
}

.demo-body { background: var(--stage-face); }

/* In the muted grey, not the text colour: lit, a worked example of a question
   would compete with the claim in the headline for the first look. Size and the
   quote marks are enough to say it is the question. */
.poster-q {
  margin: 0;
  /* 40ch, so the question leaves the end of its row to the presses beside it. */
  max-width: 40ch;
  font-size: clamp(17px, 2.2vw, 20px);
  line-height: 1.5;
  color: var(--muted);
  text-wrap: balance;
}

/* Quote marks rather than a bubble: a bubble here would be the first turn of a
   conversation that has not started, and the run underneath draws that turn
   itself a second later. */
.poster-q::before { content: "\201C"; }
.poster-q::after { content: "\201D"; }

/* Everything the play button is holding back. It is display rather than
   visibility or a height: the page's gaps come from the landing grid, and a
   stowed section that still counts as a track leaves a 64px hole where it is
   going to appear. */
.stowed { display: none; }

/* The loud press: drawn rather than filled with the accent, its border the
   accent at full strength while every other border on the page is the line
   grey. A solid accent block would be the one opaque colour on a page that is
   otherwise words over water.

   What wears it on the landing page is the way in — "create account" in the
   corner and again at the foot of the page, both of them /signup. The loud one
   of a pair is how a reader is told which of two doors is theirs. It is also on
   the play glyph, where `.icon-only` below takes the border and the ground back
   off and only the colour survives.

   The ground is flat and opaque rather than translucent over a backdrop blur,
   which is what the buckets and the login panel wear. Standing on bare water
   the drawing runs straight through the words, so a ground is needed — but a
   blurred one is composited on a layer of its own whose bounds round to whole
   device pixels, and on iOS a 1px outline sitting exactly on those bounds gets
   rounded off: the presses come up missing an edge until something forces a
   repaint. It is worst here, where what would be blurred is a fixed canvas the
   page never scrolls. Moving the blur to a pseudo-element does not help, since
   the promotion reaches the whole button. The panel colour flat is what the
   blur was approximating anyway, and it costs only the water showing faintly
   through a press.

   The corner is the one the page already uses — 8px, the radius on the link in
   the top right and on every button in the app. A pill would be a shape this
   page has nowhere else. */
.btn-lead {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 12px 22px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: var(--panel);
  color: var(--accent);
  font-size: 15px;
  font-weight: 550;
  letter-spacing: -0.005em;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 0.16s ease;
}

/* The one size above the default, worn by whichever of the two presses is
   standing alone at the end of the page. Written for both rather than for the
   lead alone: which treatment that press wears is a question about how loud it
   should be, and it should not also change how big it is. */
.btn-lead.large,
.btn-quiet.large { padding: 14px 26px; font-size: 16px; }

/* And the one below it, for the corner of the page, where the pair of presses
   has to be smaller than anything else in the row it shares with the name.
   Written for both treatments for the reason the large one is. */
.btn-lead.small { padding: 7px 14px; font-size: 13px; }

/* Hover fills, faintly, and that is the whole of it: the button stays exactly
   where it is. A control that moves under the pointer is answering before it
   has been pressed.

   Behind `hover: hover` because a finger has no pointer to leave with. A
   touchscreen resolves `:hover` on tap and holds it until something else is
   tapped, which would leave every pressed button lit as though the thumb were
   still on it — a control stuck in a state rather than one that has been
   pressed. Gated here, a phone never enters the state, and `:active` below is
   what answers the press. */
@media (hover: hover) {
  .btn-lead:hover { background: color-mix(in srgb, var(--accent) 14%, var(--panel)); }
}

/* Ungated on purpose: this is the press itself rather than the approach to it,
   and it is the whole of the feedback a thumb gets. It ends when the finger
   lifts. */
.btn-lead:active { background: color-mix(in srgb, var(--accent) 22%, var(--panel)); }

/* The second offer beside the first, and readable as second at a glance: the
   same shape, ground and outline, in the line grey against the lead's accent,
   with the muted text to match. It comes up to the text colour on hover, which
   is the whole of its emphasis.

   The corner of the page wears this too, and only this — the login, the way to
   make an account, and the way back to the chat for whoever is already in.
   Those are the smallest controls on a page of drawing, so they are buttons or
   they are lost in a bright patch of water. */
.btn-quiet {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--muted);
  font-size: 14px;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.16s ease, border-color 0.16s ease, background-color 0.16s ease;
}

.btn-quiet.small { padding: 7px 14px; font-size: 13px; }

@media (hover: hover) {
  .btn-quiet:hover {
    color: var(--text);
    border-color: color-mix(in srgb, var(--line) 55%, var(--text));
    background: var(--panel-2);
  }
}

/* What the pointer gets on hover, a thumb gets for as long as it is down. */
.btn-quiet:active {
  color: var(--text);
  border-color: color-mix(in srgb, var(--line) 55%, var(--text));
  background: var(--panel-2);
}


/* Every glyph in these buttons is an inline SVG rather than a character: ⇄ and
   ↺ are an emoji on half the platforms that have them, and these have to sit at
   the height of the word beside them. They size off that word, so the pair
   scales together. */
.btn-glyph {
  flex: none;
  width: 1em;
  height: 1em;
}

/* The pair on the poster: the glyph and nothing else. No box around either —
   a rectangle drawn on a page of line drawing is one more rectangle, and what
   makes the triangle pressable is that it is a triangle under a question. The
   square around each is still 54px, since a press is a thumb whether or not
   anything is drawn where the thumb lands.

   Dropping the word costs nothing here and only here, the question they act on
   being directly above them. It is still carried on `aria-label` and `title`. */
.btn-lead.icon-only, .btn-quiet.icon-only {
  justify-content: center;
  width: 54px;
  height: 54px;
  padding: 0;
  gap: 0;
  border-color: transparent;
  background: none;
}

.btn-lead.icon-only .btn-glyph, .btn-quiet.icon-only .btn-glyph {
  width: 30px;
  height: 30px;
}

/* With no box to fill, hover is the glyph itself coming up — and on a phone,
   where there is no hover, the same is true of the press. Both states say the
   same thing, since without a box the glyph is the button and so the glyph is
   what answers.

   This pair is the shuffle and the play glyph on the poster, where a tapped
   shuffle held at the text colour would read as stuck while the question under
   it changed. */
.btn-lead.icon-only:active { background: none; }
@media (hover: hover) {
  .btn-lead.icon-only:hover { background: none; }
  .btn-quiet.icon-only:hover {
    background: none;
    border-color: transparent;
    color: var(--text);
  }
}

.btn-quiet.icon-only:active {
  background: none;
  border-color: transparent;
  color: var(--text);
}

.btn-lead:focus-visible, .btn-quiet:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* A class's worth of `display` outranks the browser's own [hidden] rule, so the
   press that takes the play button away has to be spelled out here. */
.btn-lead[hidden] { display: none; }

/* The pair of presses, on the far side of the line they act on: the question
   takes the width it needs and these take the end of the row. */
.poster-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0;
  justify-self: end;
}

/* Inside the frame the stage has no frame of its own: a box inside a box is
   two boxes, and the outer one is the object. Padding and nothing else, so the
   run stands off the drawn rules by about what the still does. */
.demo-stage { padding: 18px 24px 6px; }

/* Neither sticky nor a gradient in here: both belong to a composer at the
   bottom of a scrolling page, and this one is at the bottom of a box. */
.demo-composer { position: static; background: none; padding: 14px 0 0; }
#demo-send { transition: transform 0.12s ease; }
#demo-send.pressed { transform: scale(0.94); }

/* The cross, in the bottom right corner of the frame it closes. Right-aligned
   rather than centred or led with: it is the way out, and a way out belongs
   where the eye lands after the reading is done rather than in front of it. */
.demo-controls {
  display: flex;
  justify-content: flex-end;
  margin: 0;
  padding: 0 18px 14px;
}

/* The demo hands the visitor one thing to do, at the end of an answer they
   have just watched arrive, and the button that does it is deliberately quiet
   everywhere else in the app. So it arrives wearing the accent and throws
   rings for long enough to outlast reading the answer above it — about half a
   minute — and then stops, because a control that pulses forever is a thing to
   be endured rather than pressed. The colour stays after the rings are done,
   which is the part still saying "this one". */
button.validate.waiting {
  color: var(--accent);
  border-color: var(--accent);
  animation: validate-pulse 1.5s ease-out 20;
}

/* Two rings rather than one, the second half a beat behind the first, and the
   button leans forward as they leave: a single ring at this size reads as a
   glow on a dark background, and it is the movement that carries across a
   page. The scale is small enough to stay inside the row it sits in — the
   footer holds its height, so nothing below the button moves with it. */
@keyframes validate-pulse {
  0% {
    transform: scale(1);
    box-shadow: 0 0 0 0 var(--accent-soft), 0 0 0 0 var(--accent-soft);
  }
  18% { transform: scale(1.05); }
  55% {
    box-shadow: 0 0 0 16px transparent, 0 0 0 6px var(--accent-soft);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 0 0 16px transparent, 0 0 0 20px transparent;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* No rings, but the button keeps the colour: the cue is what matters, the
     motion is only how it was delivered. */
  button.validate.waiting { animation: none; }
}

/* The legend for the buckets, which the script un-stows once the verdict it
   explains is on the screen. A heading at the weight of a label rather than of
   a section: what is under it is the key to something already read, not the
   next thing to read. */
.buckets h2 {
  margin: 0 0 16px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--muted);
}

.bucket-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.bucket-cards li {
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--panel) 78%, transparent);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
}
.bucket-cards p { margin: 10px 0 0; color: var(--muted); font-size: 14px; }
.bucket-cards b { color: var(--text); }

/* The page's one call to action, at the end of it, where a visitor has already
   seen what they would be logging in to. It is worded as the next question
   rather than as the door, so it and the quiet link up in the corner are never
   two ways of saying the same word. A rule over the top, because this is the
   page stopping rather than another section of it — and centred, which is the
   only centred thing here and so reads as the end. */
.closing {
  border-top: 1px solid var(--line);
  padding-top: 46px;
  text-align: center;
}

/* Lighter than a heading's default: weight has to fall as the page goes down,
   or the smallest heading on it is the loudest thing on it. 400 rather than the
   hero's 300, since light at 26px reads as faint where the same weight at 40
   reads as composed.

   In the muted grey, so nothing down here asks to be found: the claim at the
   top is what the page is for, and this is the offer waiting for a reader who
   has already decided to take it. The size is then what keeps it a heading —
   26px against the 15 of the line beneath it, which is the same grey and would
   otherwise read as one paragraph with it. */
.closing h2 {
  margin: 0 0 10px;
  font-size: clamp(21px, 2.6vw, 26px);
  font-weight: 400;
  letter-spacing: -0.02em;
  color: var(--muted);
}

.closing .muted { margin: 0 auto 26px; max-width: 46ch; }

.hero-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  flex-wrap: wrap;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  #demo-send { transition: none; }
  /* The colours still change on hover; only the fading between them goes. */
  .btn-lead, .btn-quiet { transition: none; }
}

/* --- the rail ---------------------------------------------------------------- */

/* Fixed to the left edge and the full height of the window, in the order the
   eye takes it: the one action, the chats, and the account at the far end.
   Only the list of chats scrolls — the two ends stay put, so the way out of a
   long list is never itself scrolled away.

   `visibility` rides along with the transform when it goes: without it the
   panel is still reachable by tab and by screen reader from off the side of
   the screen. */
.sidebar {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 60;
  width: var(--rail);
  display: flex;
  flex-direction: column;
  padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
  /* Room above the first row. The rail is a column that fills from the top, and
     starting it in the very first pixel available reads as everything having
     been pushed up there rather than as the top of a list. */
  padding-top: max(var(--top-room), env(safe-area-inset-top));
  background: var(--panel);
  border-right: 1px solid var(--line);
  transition: transform 0.22s ease, visibility 0.22s;
}

/* Collapsing is a wide-screen decision and is kept as one: on a narrow screen
   the rail is a drawer, opened and shut per visit, and a collapse remembered
   from a desktop must not be able to hold that drawer closed. Stated as its
   own query rather than left to the cascade — `:root.rail-hidden .sidebar`
   outranks `.sidebar.open`, so the drawer would lose. */
@media (min-width: 901px) {
  :root.rail-hidden .sidebar { transform: translateX(-100%); visibility: hidden; }
}

/* The button against the closing edge, and the water taking the rest of the
   line. Nothing else is up here: a new chat is a corner of the conversation,
   and the name is never in the rail. */
.sidebar-head {
  display: flex;
  align-items: center;
  min-height: var(--top-row);
  gap: 10px;
  /* No inset of its own: the button's edge is the chat rows' edge, so the two
     things in the rail that light up under a pointer light up in the same
     column. */
  padding: 0;
}

/* Whatever the row has left over, and no more: `min-width: 0` because a flex
   item's floor is its own content and an SVG's content is its viewBox — without
   it the drawing would hold all 258 units of itself open and push the button
   off the panel on a narrow drawer.

   `slice` on the SVG crops what will not fit rather than squashing it, so the
   water is drawn at one size at every width and only the far end of it goes; a
   wave stretched to fit would be a different sea every time the window moved.
   The crop is at the left, and the mask makes it the drawing coming in off the
   edge of the panel rather than a line cut by it.

   Mixed down from the colour the rail's own text is set in rather than up from
   the colour its edges are: at 1.3px a line needs more contrast than a border
   does to read as a line at all, and in `--line` this would be a texture you
   had to know was there. Two thirds of the way to the titles is far enough to
   be seen and still short of the first thing in the rail worth reading. Said as
   a mix rather than an opacity, so the mask stays the only thing varying the
   alpha. */
.head-sea {
  flex: 1;
  min-width: 0;
  height: 33px;
  color: color-mix(in srgb, var(--muted) 66%, var(--panel));
  mask-image: linear-gradient(to right, transparent, #000 42%);
}

/* The list takes what is left between the head and the account, and scrolls
   inside it. `contain` so reaching the end of the chats does not carry on into
   the conversation behind them. */
.chat-list {
  list-style: none;
  margin: 12px -4px 0;
  padding: 0 4px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: grid;
  /* minmax(0, …) rather than the 1fr a grid column is given by default: that
     default floor is the widest thing in the column, and every row here holds
     a whole first message set on one line. Left at it, the rows grow to fit the
     longest title and the rail scrolls sideways instead of cutting them. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
  align-content: start;
}

/* Nothing saved yet: the note belongs at the top of the column, not pushed to
   the foot of it by a list stretching over nothing — and on the same column as
   the chats it is standing in for. */
.chat-list:empty { flex: 0 0 auto; }
#chat-list-empty { margin: 0; padding: 4px 10px; }

/* When, written once for the run of chats under it. It is the only thing in
   the rail that is not a chat, so it is set as far down as a label can go and
   still be read: the rail should look like one column of titles with the dates
   noticed second. */
.chat-group {
  padding: 0 10px;
  margin: 16px 0 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* The first date has the head's own gap above it already. */
.chat-group:first-child { margin-top: 0; }

.chat-item {
  display: flex;
  align-items: center;
  position: relative;
  border-radius: 8px;
}
.chat-item:hover { background: var(--panel-2); }

/* The chat you are in. A filled row rather than a mark down its edge: with the
   rows a line tall and a pixel apart, the column reads as one block of text and
   the thing that has to stand out of it is the whole row. */
.chat-item.current { background: var(--panel-2); }

/* One line, and the whole width of the rail for it. The title is a first
   message and will not fit; what does not fit is cut rather than wrapped,
   because a rail that grew to fit a long question would push the chat around
   every time one was asked. */
.chat-open {
  flex: 1;
  min-width: 0;
  padding: 7px 10px;
  border: 0;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 14px;
  line-height: 1.45;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Both fills are the same panel, so the weight is what tells the chat you are
   in apart from the one under the pointer — a pointer can only be on one row at
   a time, and whichever it is, the current one still reads as the current one. */
.chat-item:hover .chat-open { color: var(--text); }
.chat-item.current .chat-open { color: var(--text); font-weight: 500; }

/* Kept off the row until the row is reached, and taken out of the flow while it
   is away so that the title has the full width to run out in rather than
   stopping short of a gap that is empty most of the time. */
.chat-delete {
  position: absolute;
  right: 2px;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 17px;
  line-height: 1;
  padding: 5px 8px;
  border-radius: 6px;
  cursor: pointer;
  opacity: 0;
}
.chat-item:hover .chat-delete, .chat-delete:focus-visible { opacity: 1; }
.chat-delete:hover { color: var(--disputed); }

/* The title runs the full width of the row and is faded out from under the
   delete rather than stopped short of it: reserving the space permanently would
   cut every title 30px early for a button that is only there while the pointer
   is, and reserving it on hover would re-cut the title under the cursor. The
   transparent stop clears the button, so the two never overlap. */
.chat-item:hover .chat-open {
  mask-image: linear-gradient(to left, transparent 32px, #000 56px);
}

.bubble-text.interrupted { color: var(--muted); font-style: italic; }

/* --- account ---------------------------------------------------------------- */

/* At the foot of the rail, after everything it is not: the address is a label
   you check, not a control you reach for. No rule over it — the run of empty
   panel between the last chat and this one says the same thing without adding
   another line to the column. */
.account {
  position: relative;
  margin-top: 10px;
}

.account summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 9px;
  /* The chat rows' own column: the initial starts where the titles start. */
  padding: 6px 8px;
  border-radius: 8px;
  color: var(--muted);
  font-size: 13px;
}
.account summary::-webkit-details-marker { display: none; }
.account summary:hover, .account[open] summary { color: var(--text); background: var(--panel-2); }

/* The initial standing in for the address wherever there is no room for one,
   and beside it wherever there is. */
.avatar {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--line);
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}

/* One line, however long the address: a rail that grew to fit an email would
   take the width out of the conversation. */
.account-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Upwards, because there is nothing below it but the bottom of the window. */
.account-menu {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 6px);
  z-index: 10;
  display: grid;
  gap: 4px;
  min-width: 170px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
}
.account-menu a, .account-menu button {
  padding: 6px 8px;
  border-radius: 6px;
  text-align: left;
  font-size: 14px;
  color: var(--text);
  text-decoration: none;
}
.account-menu a:hover, .account-menu button:hover { background: var(--panel-2); }
.account-menu form { margin: 0; }
.account-menu button { width: 100%; border: 0; background: none; font: inherit; cursor: pointer; }

/* --- narrow screens ---------------------------------------------------------- */

/* Below this the rail and the chat stop fitting side by side, so the things
   that are not the conversation stop being on screen rather than merely getting
   shorter — otherwise a phone opens on four rows of chrome above an empty chat.
   The rail becomes a drawer over the page, setup stays a sheet on the composer,
   and both are one tap from the two icons at the top of the chat. */
@media (max-width: 900px) {
  .site-header {
    /* No wrapping: the bar is one row by construction. */
    flex-wrap: nowrap;
    gap: 4px;
    padding: 10px 14px;
    padding-top: max(10px, env(safe-area-inset-top));
  }

  /* The icons are over the top of the page from here down, so a panel scrolled
     to the top of the screen goes under them. 36 of the 52 is the button
     itself, the rest is the padding around it and the gap the wide layout
     leaves, with the notch on top of both. */
  .validation { scroll-margin-top: calc(52px + env(safe-area-inset-top)); }

  .site-tag { display: none; }
  /* The one auto margin in the bar: the name holds the left, whatever else the
     page put up there holds the right. Scoped to the bar — the landing page's
     lockup is the page's own first line and keeps its size down here. */
  .site-header .brand { font-size: 19px; margin-right: auto; }

  /* The page belongs to the chat, and the rail is over it rather than beside
     it — so nothing is set aside for the rail and the icons that call it back
     are always up. */
  body.app { --rail-w: 0px; }
  /* Narrower margins, and the same nothing along the bottom: what is under the
     composer is set on the composer. Named through the pane, because the
     landing page's demo wears this class too and has its own foot. */
  .pane { padding: 0 14px; }
  .pane .composer { padding-bottom: 14px; }
  /* The line the two columns share is a wide-screen arrangement: down here the
     rail is a drawer over the page and there is nothing to line up with, so the
     chat takes its top back. */
  .pane-top {
    padding-top: max(10px, env(safe-area-inset-top));
    min-height: 0;
  }
  /* The rail is a drawer down here, so it is always away and the icon that
     calls it back is always up. Both corners come up with the name rather than
     holding the wide layout's drop, which is room bought to line the name up
     with a rail that is not beside it here. */
  .pane-tools { display: flex; }
  .pane-tools, .pane-actions {
    padding-top: max(10px, env(safe-area-inset-top));
    min-height: 0;
  }
  /* The drawer's own head keeps the same line, so the button does not jump
     down the screen between being pressed and being pressed again. */
  .sidebar { padding-top: max(10px, env(safe-area-inset-top)); }
  /* Tighter than the wide layout's gap, because vertical room is the scarce
     thing on a phone — but still the whole of the fade, which is not
     negotiable at any width. */
  .chat { padding-top: calc(var(--top-fade) + 4px); }

  /* Off-canvas rather than merely hidden, so a tap outside it is the way back
     out. It is the same rail, moved: the width narrows to a phone's, the
     shadow says it is over the page rather than part of it, and the state that
     is remembered on a wide screen has no say down here. */
  .sidebar {
    width: min(300px, 84vw);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
    transform: translateX(-100%);
    visibility: hidden;
  }
  .sidebar.open { transform: none; visibility: visible; }

  .scrim {
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(0, 0, 0, 0.55);
  }

  body.drawer-open { overflow: hidden; }

  /* A thumb's row rather than a pointer's, and the delete always shown: there
     is no hover down here to reveal it with, so the title makes room for it
     instead of running under it. */
  .chat-open { padding: 10px; padding-right: 38px; }
  .chat-delete { opacity: 1; }
  .chat-item:hover .chat-open { mask-image: none; }
}

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

@media (max-width: 640px) {
  /* Anything under 16px is a font iOS Safari zooms the page in to read, and it
     does not zoom back out afterwards. */
  select, textarea, input:not([type="checkbox"]) { font-size: 16px; }

  .bubble.user { max-width: 90%; }

  /* One line under an answer, not two: the axes box comes out when asked.
     `:not([hidden])` because this rule would otherwise outrank the `hidden`
     attribute on an answer that has already been validated. */
  button.axes-toggle:not([hidden]) { display: inline-block; }
  .validate-row .validate-fields { display: none; }
  .validate-row.axes-open .validate-fields { display: flex; width: 100%; }
  .validate-row.axes-open input.axes,
  .validate-row.axes-open input.option { width: 100%; }

  /* The one thing the page is asking to be pressed, at a size a thumb can
     land on: 13px text in 5px of padding is a 32px button, and a finger is
     wider than that. */
  button.validate { padding: 9px 14px; }

  .validation { padding: 12px; }
  .finding { grid-template-columns: auto 1fr; }
  .finding-count { grid-column: 2; }

  /* Hide, show all, and the rewordings fold are text the size of text, and a
     tap that misses one of them lands on the claim underneath. The padding
     buys the hit area and the negative margin gives the space back, so the
     panel is laid out exactly as it was and only the targets are bigger. The
     fold keeps its own display: a summary that is told to be anything but a
     list item loses its triangle. */
  .validation button.link { padding: 8px 6px; margin: -8px -6px; }
  .validation .aside summary { padding: 8px 0; margin: -8px 0; }

  /* The composer keeps its shape down here — one line, a round button at each
     end — and only what is under it changes, for the phone's own bottom edge. */
  .composer { padding: 10px 0 max(10px, env(safe-area-inset-bottom)); }

  /* The landing page down a phone: one column of cards, a headline that fits
     the screen, and a stage that spends its width on the conversation rather
     than on its own padding. The composer line comes after the rule above,
     which sets the same shorthand for the one at the foot of the chat. */
  .landing { padding: 6px 0 64px; gap: 44px; }
  /* The lockup holds its size down here — it is the one thing on the page that
     has to survive the visit — and only the room around it gives way. */
  .page-brand { gap: 10px; padding-bottom: 32px; }
  .eyebrow { margin-bottom: 14px; }
  .hero h1 { max-width: none; }
  /* Less room above the sub: the screen is short and the space is the demo's. */
  .hero-sub { margin-top: 14px; font-size: 16px; }
  /* The stage runs to both edges of the screen down here. A phone is already a
     narrow column inside the page's margins, and spending those margins on the
     frame of the run takes them from the run — which is the thing a visitor
     came to see, and the only thing here that gets wider by being let out.

     The bleed is the page's own margin, negated, so the two drawn rules end
     exactly on the edges of the screen and the water they are drawn on ends
     with them. `body.drawn main` clips at that same line, so there is nothing
     out there to be dragged sideways to.

     What it gives back as padding is the same number again, which is the point
     of doing it this way rather than by widening the box: the frame moves out
     to the edges and the words do not move at all. The question, the bubbles
     and the composer stay on the left edge the headline is on. */
  .stagebox { margin-left: calc(-1 * var(--page-pad)); margin-right: calc(-1 * var(--page-pad)); }

  /* A question and two presses side by side need a column wide enough to be
     side by side in. Down here the still is a stack, and the whole stack is
     centred — the question over the presses, both on the middle of the frame.

     Centred rather than ranged left, which is what the rest of this page does
     and what the frame does at every wider size. A stack whose two parts are
     the same shape reads as one object, and a phone's frame is nearly the
     screen: there is no column edge left beside it for the left edge of the
     question to answer to. So it answers to the frame instead, and the presses
     sit under the middle of what they act on rather than under the first words
     of a sentence they are not part of. */
  .poster {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
    /* Less under the presses than over the question: the glyph buttons are
       44px boxes around 24px marks, so they bring about ten pixels of their
       own to the bottom of the frame before the padding starts. */
    padding: 22px var(--page-pad) 14px;
    gap: 18px;
  }
  .poster-actions { justify-self: center; }

  /* Smaller, because a 54px square holding a 30px glyph is drawn for a row it
     shares with three lines of question. Alone under the question it is the
     only thing on its line and the largest object in the frame. 44 is still
     the whole of a thumb — it is the box that comes down, not the target. */
  .btn-lead.icon-only, .btn-quiet.icon-only { width: 44px; height: 44px; }
  .btn-lead.icon-only .btn-glyph, .btn-quiet.icon-only .btn-glyph {
    width: 24px;
    height: 24px;
  }
  .bucket-cards { grid-template-columns: minmax(0, 1fr); }
  .demo-stage { padding: 14px var(--page-pad) 4px; }
  .demo-controls { padding: 4px var(--page-pad) 14px; }
  .demo-composer { padding: 10px 0 0; }
  .closing { padding-top: 34px; }
  /* A phone is a tall narrow crop of a wide drawing, which is most of the
     drawing gone. Less of it, so what is left reads as the water's texture
     rather than as a piece of something cut off. */
  .art { opacity: 0.6; }
}
