/* Every colour on the page comes from here, and nowhere else uses a literal one. That is what makes
   a theme possible at all: anything reading a hex directly would keep its daylight self in every
   other palette.

   Only five of these are given. The rest are mixed from them, which is what keeps a palette honest:
   a border is the ink at twelve percent whether the page is white or nearly black, so a theme is
   five colours to choose rather than thirty to keep in agreement. It also means the names can say
   what a colour is for instead of what it looks like - `--rail` is the quiet surface behind the
   sidebar, and in a dark palette that is lighter than the page, not darker. */
:root {
  --bg: #ffffff;
  --ink: #201f1e;
  --accent: #5b5fc7;
  --accent-ink: #ffffff;
  --bad: #a4262c;
}

/* Mixed from the five. Held apart from them so that a theme overrides the seed and inherits all of
   this untouched - put these in the same block and every palette would have to restate them. */
:root {
  --panel: var(--bg);                                     /* raised: the box, buttons, cards */
  --rail: color-mix(in srgb, var(--ink) 3%, var(--bg));   /* sunk: the sidebar, a card's body */
  --field-bar: color-mix(in srgb, var(--ink) 5%, var(--bg));
  --line: color-mix(in srgb, var(--ink) 12%, var(--bg));

  --muted: color-mix(in srgb, var(--ink) 72%, var(--bg));
  --faint: color-mix(in srgb, var(--ink) 55%, var(--bg));

  --mine: color-mix(in srgb, var(--ink) 6%, var(--bg));   /* what you said, and code */
  --hover: color-mix(in srgb, var(--ink) 7%, var(--bg));
  --hover-deep: color-mix(in srgb, var(--ink) 11%, var(--bg));

  --accent-deep: color-mix(in srgb, var(--accent) 88%, var(--ink));  /* the accent, pressed */
  --accent-soft: color-mix(in srgb, var(--accent) 12%, var(--bg));   /* the row you are on */
  --accent-wash: color-mix(in srgb, var(--accent) 5%, var(--bg));
  --accent-line: color-mix(in srgb, var(--accent) 28%, var(--bg));

  --bad-ink: #ffffff;
  --bad-deep: color-mix(in srgb, var(--bad) 85%, var(--ink));
  --bad-line: color-mix(in srgb, var(--bad) 30%, var(--bg));
  --bad-wash: color-mix(in srgb, var(--bad) 7%, var(--bg));

  /* A found search term, and the two overlays. These stay put in every palette: a highlighter is
     yellow with dark type on it wherever it is, and what goes over an image is dark because the
     image underneath is not ours to lighten. */
  --mark: #fff3b0;
  --mark-ink: #201f1e;
  --tint-ink: #ffffff;    /* your initials, on the colour drawn from your name */
  --scrim: rgba(0, 0, 0, 0.35);
  --veil: rgba(0, 0, 0, 0.65);
  --veil-ink: #ffffff;
  --shadow: rgba(0, 0, 0, 0.25);

  /* Six colours for the six series a chart may hold. They have to stay apart from each other, so
     unlike everything above they are hues in their own right rather than mixes of the accent -
     six shades of one hue is a chart nobody can read.
     
     Each is then pulled a quarter of the way towards the ink, which is what makes them work in
     twelve palettes instead of one: on a white page the ink is nearly black and the line darkens
     to sit on it, and on a dark page the ink is nearly white and the same declaration lifts it.
     The seed colours are never drawn as written. */
  --plot-1: color-mix(in srgb, #5b5fc7 74%, var(--ink));
  --plot-2: color-mix(in srgb, #d67548 74%, var(--ink));
  --plot-3: color-mix(in srgb, #2f9e79 74%, var(--ink));
  --plot-4: color-mix(in srgb, #b8496e 74%, var(--ink));
  --plot-5: color-mix(in srgb, #8a6ec4 74%, var(--ink));
  --plot-6: color-mix(in srgb, #3e91c4 74%, var(--ink));

  /* One column for the thread, the box, the welcome strip and the panes. They are all the same
     reading width, so it is one number rather than five that drift apart. */
  --column: 940px;

  /* The header is a fixed height rather than whatever its contents come to, because the resting
     page measures from the top of the window and has to know where the window stops being header.
     A ratio would drift the moment anything in there changed line count. */
  --header: 64px;
}

/* ------------------------------------------------------------------- palettes

   Twelve, six of them light and six dark, each a seed of five colours. They are written against an
   attribute rather than against `html` on purpose: setting it on the document themes the page, and
   setting it on one swatch in the menu themes that swatch. The mixes above are declared on `:root`
   but substituted where they are used, so a swatch overriding the seed gets every derived colour
   recomputed for it - the menu cannot show a palette that is not the one it will apply. */

/* Daylight is the seed above, and also has to be written out here. The document without a theme
   attribute is Daylight, so the page needs no block - but a swatch offering Daylight does, or it
   inherits whatever palette is on at the time and shows the dark page it is meant to replace. */
[data-theme="daylight"] {
  --bg: #ffffff; --ink: #201f1e; --accent: #5b5fc7; --accent-ink: #ffffff; --bad: #a4262c;
}

[data-theme="stormy-morning"] {
  --bg: #eef1f5; --ink: #22303f; --accent: #3f6d9e; --accent-ink: #ffffff; --bad: #a4262c;
}
[data-theme="sea-glass"] {
  --bg: #eff7f4; --ink: #17302b; --accent: #2e8b7a; --accent-ink: #ffffff; --bad: #b03a3a;
}
[data-theme="lavender-haze"] {
  --bg: #f7f4fb; --ink: #2a2338; --accent: #7a5cc4; --accent-ink: #ffffff; --bad: #a4262c;
}
[data-theme="desert-rose"] {
  --bg: #fdf3ef; --ink: #3a2622; --accent: #c2634f; --accent-ink: #ffffff; --bad: #9e2b2b;
}
[data-theme="old-parchment"] {
  --bg: #f8f4e9; --ink: #33301f; --accent: #8a6d2f; --accent-ink: #ffffff; --bad: #a4262c;
}

[data-theme="midnight"] {
  --bg: #16181d; --ink: #e8eaed; --accent: #7f83e0; --accent-ink: #12141a; --bad: #e06c75;
}
[data-theme="blue-eclipse"] {
  --bg: #0f1830; --ink: #dce4f5; --accent: #5b8dd9; --accent-ink: #0b1020; --bad: #e0707a;
}
[data-theme="lush-forest"] {
  --bg: #101c17; --ink: #dceae2; --accent: #4fae7f; --accent-ink: #08130e; --bad: #e07a6a;
}
[data-theme="chocolate-truffle"] {
  --bg: #1e1613; --ink: #ece0d8; --accent: #c08a5e; --accent-ink: #1a120e; --bad: #e07a6a;
}
[data-theme="chilli-spice"] {
  --bg: #1c1210; --ink: #f0dedb; --accent: #e05a3c; --accent-ink: #1a0f0c; --bad: #ff7a6b;
}
[data-theme="amber-dusk"] {
  --bg: #1d1a12; --ink: #efe6d2; --accent: #d9a441; --accent-ink: #1a1509; --bad: #e0725c;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  display: flex;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 "Segoe UI", system-ui, -apple-system, sans-serif;
}

/* Everything that is not the sidebar. A column of its own so the sidebar keeps full height and
   does not scroll with the conversation beside it. */
.shell {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------- the sidebar

   Where to go and what has already been said. It is a fixed column rather than a drawer because
   the list of conversations is the thing this page grew a sidebar for: hiding it behind a button
   would make going back to yesterday's question a two-step action again. */
#blade {
  flex: 0 0 272px;
  width: 272px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--rail);
  border-right: 1px solid var(--line);
  padding: 12px 8px;
  gap: 2px;
}

.blade-nav { display: flex; flex-direction: column; gap: 2px; }

/* The mark on the door. It carries the version because that is the one fact anybody reporting
   something odd is asked for, and it should not take a page to find. */
.blade-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 10px 14px;
}

/* On a phone the sidebar would take two thirds of the width, so it narrows to the icons alone: the
   destinations stay one tap away and the list of conversations moves to the Sessions pane, which is
   where a long list wants to be read anyway. */
@media (max-width: 640px) {
  #blade { flex-basis: 52px; width: 52px; padding: 12px 6px; }
  #blade .blade-label, #blade .sessions, #blade .blade-section h2, #blade .titles { display: none; }
  .blade-brand { justify-content: center; padding: 4px 0 12px; }
  .blade-item { justify-content: center; padding-left: 0; padding-right: 0; }
  .blade-section { justify-content: center; }
}

.blade-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--ink);
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}
.blade-item svg { width: 18px; height: 18px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.3; stroke-linecap: round; }
.blade-item:hover { background: var(--hover); }
.blade-item.current { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* ------------------------------------------------------------------ the menu

   Three across, which is what makes it a set of colours to look at rather than a list of names to
   read: the eye compares neighbours, and twelve in a single column would be twelve decisions in a
   row instead of one. */
.theme-menu {
  position: fixed;
  z-index: 40;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  width: 318px;
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 8px 24px var(--shadow);
}
.theme-menu[hidden] { display: none; }

.theme-option {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.theme-option:hover { background: var(--hover); }
.theme-option[aria-checked="true"] { border-color: var(--accent); background: var(--accent-soft); }

.theme-name { font-size: 11.5px; line-height: 1.3; color: var(--muted); }
.theme-option[aria-checked="true"] .theme-name { color: var(--accent); font-weight: 600; }

/* The swatch wears the palette it offers. Because it carries the theme attribute itself, every
   mixed colour is recomputed inside it - so this is the palette rather than an impression of it,
   and it cannot fall out of step with what pressing it does. */
.theme-swatch {
  position: relative;
  height: 34px;
  border-radius: 5px;
  background: var(--bg);
  border: 1px solid var(--line);
  overflow: hidden;
}
.theme-swatch::before,
.theme-swatch::after {
  content: "";
  position: absolute;
  left: 7px;
  border-radius: 3px;
}
.theme-swatch::before { top: 8px; width: 15px; height: 4px; background: var(--ink); }
.theme-swatch::after { top: 17px; width: 24px; height: 8px; background: var(--accent); }

/* The heading over the list. It is deliberately not a button - there is nothing behind the word
   "Sessions" to open - so it is styled as the label it is, with the two actions beside it. */
.blade-section {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 14px 0 4px;
  padding: 0 10px 0 12px;
}
.blade-section h2 {
  flex: 1 1 auto;
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--faint);
}
.blade-section-tools { display: flex; gap: 2px; }

.icon-button {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.icon-button:hover { background: var(--hover-deep); color: var(--ink); }
.icon-button svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
/* The one that empties the list, set slightly apart from the two that do not and turning red under
   the pointer. Three identical buttons in a row is how somebody clears everything while reaching
   for a new conversation, and the gap is cheaper than the apology. */
.icon-button.danger { margin-right: 4px; }
.icon-button.danger:hover {
  background: color-mix(in srgb, var(--bad) 12%, transparent);
  color: var(--bad);
}

.sessions {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* A row is the conversation plus the way to be rid of it. The delete only appears under the
   pointer: it is destructive, it sits next to the thing that opens the conversation, and a
   permanently visible one is a target somebody eventually hits by accident. */
.session-row {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  border-radius: 6px;
}
.session-row:hover { background: var(--hover); }
.session-row.current { background: var(--accent-soft); }

.session {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 4px 7px 10px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--ink);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.session-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session.current { color: var(--accent); }

/* Which conversation is working. A ring that turns while it is, and a filled dot once it has
   settled - so a slow answer can be left running and found again from across the sidebar. */
.session-mark {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}
.session-mark.settled { background: var(--faint); }
.session-mark.working {
  background: none;
  border: 2px solid var(--accent-line);
  border-top-color: var(--accent);
  animation: turning 0.8s linear infinite;
}
@keyframes turning { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .session-mark.working { animation-duration: 3s; }
}

.session-drop {
  flex: 0 0 auto;
  display: none;
  padding: 4px;
  margin-right: 5px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--muted);
  cursor: pointer;
  line-height: 0;
}
.session-drop svg { width: 14px; height: 14px; }
.session-row:hover .session-drop,
.session-drop:focus-visible { display: block; }
.session-drop:hover { background: var(--bad-wash); color: var(--bad); }
.session-drop:disabled { opacity: 0.4; cursor: default; }

.sessions-empty { padding: 6px 10px; font-size: 12px; color: var(--faint); }

header {
  display: flex;
  align-items: center;
  gap: 12px;
  height: var(--header);
  padding: 0 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

/* Not cropped to a circle. Lexi's picture is a drawn mark rather than a photograph of a face, and a
   circle takes the corners off the drawing - so it is shown whole, at whatever shape it was drawn.
   The same everywhere it appears: the header, the resting page, and beside every answer. */
.avatar { width: 36px; height: 36px; object-fit: contain; }
.titles { flex: 0 0 auto; min-width: 0; }
h1 { margin: 0; font-size: 16px; font-weight: 600; }
.version { font-size: 12px; font-weight: 400; color: var(--faint); }
.product { margin: 0; font-size: 12px; color: var(--muted); }

/* Who is signed in sits on the right, next to the way out. It is the same fact as the button
   beside it - this account, and how to stop being it - so it belongs there rather than under the
   agent's own name, which is not about the person at all. */
.signed-in {
  margin: 0 0 0 auto;
  font-size: 12px;
  color: var(--muted);
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

button {
  font: inherit;
  border-radius: 6px;
  border: 1px solid transparent;
  padding: 8px 16px;
  cursor: pointer;
  background: var(--accent);
  color: var(--accent-ink);
}
button:disabled { opacity: .5; cursor: default; }
button.ghost { background: transparent; color: var(--muted); border-color: var(--line); }
button.ghost:hover:not(:disabled) { background: var(--mine); color: var(--ink); }
button.small { padding: 8px 12px; font-size: 13px; }

main {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

/* display:flex outranks the hidden attribute, so anything that swaps places with something else has
   to say so explicitly or it will simply stack underneath it. */
main[hidden], .pane[hidden], #composer[hidden] { display: none; }

/* One turn's worth of page, as a box that can be taken off the thread and put back.
   display:contents so it is a handle and not a layout: the turns inside it stay direct children of
   the thread as far as the flex column is concerned, which is what stops a wrapper nobody asked for
   from changing the spacing of every conversation that has one. */
.page { display: contents; }

/* ------------------------------------------------------------- the resting page

   Nothing has been said yet, so there is nothing to scroll and no reason for the box to be pinned
   to the bottom of an empty page. The face and the box sit high instead, which is the difference
   between a page waiting for you and a page that has already gone quiet.

   The group - face, welcome and box - begins a fifth of the way down the window and runs from
   there. It is measured from the top of the window rather than from the top of the thread, so the
   header has to come out of the sum; that is the one thing --header is for. Dead centre reads as a
   splash screen, and a ratio between the two halves moved every time either of them changed. */
.hero { display: none; text-align: center; }
body.resting .hero { display: block; }
body.resting main {
  flex: 0 0 auto;
  justify-content: flex-start;
  padding-top: max(0px, calc(20vh - var(--header)));
  padding-bottom: 0;
}

/* The face is in the thread and the welcome is in the composer, so at rest their paddings meet in
   the middle of what reads as one group and pull it apart. Both are dropped here and the gap is
   left to the welcome's own margin, which is the only one that can see both sides of it. */
body.resting #composer {
  flex: 1 1 auto;
  justify-content: flex-start;
  border-top: 0;
  padding-top: 0;
}
body.resting .colophon { margin-top: auto; }

.hero-face {
  width: 72px;
  height: 72px;
  object-fit: contain;
  display: block;
  margin: 0 auto 10px;
}
.hero-name { margin: 0; font-size: 20px; font-weight: 600; }
.hero-product { margin: 2px 0 0; font-size: 13px; color: var(--muted); }

/* The welcome, over the box. It says what this is to somebody who has just arrived and then gets
   out of the way for good - Help brings the full version back into the thread.

   No box around it. A bordered grey panel is what this page uses for things that were said by
   somebody, and this was said by nobody: it is the page introducing itself. Type alone, centred
   under the face it belongs to, reads as part of the welcome rather than as the first message.

   It sits close under the face, which it belongs to, and well clear of the box underneath, which it
   does not: at the same weight and spacing on both sides it read as a line someone had typed.

   Both lines are one small grey, ranged left over the box they introduce. Two sizes made the first
   line read as a heading over the second, which is a shape this page uses for things that were said
   by somebody; and centred type under a left-ranged box is two different margins in the same eyeful.
   Quiet, aligned, and clearly not part of the conversation.

   The same width as the box, to the pixel, because that is what "over the box" has to mean: at any
   narrower measure the two left edges line up and the two right edges do not, which reads as a
   mistake rather than as a margin. It is the column every other thing on this page is set to. */
.banner {
  width: 100%;
  max-width: var(--column);
  margin: 18px auto 30px;
  padding: 0;
  text-align: left;
}
body:not(.resting) .banner { display: none; }
.banner-lead {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  letter-spacing: -.005em;
  color: var(--faint);
}
.banner-hint {
  margin: 6px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--faint);
}
.banner-hint strong { color: var(--accent); font-weight: 600; }

/* --------------------------------------------------------------------- panes

   Automations, search, and the full list of conversations. They replace the thread rather than
   sitting over it, because each of them is a place you go rather than a thing you glance at. */
.pane {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 24px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.pane-inner { width: 100%; max-width: var(--column); }
.pane h2 { margin: 0 0 4px; font-size: 18px; }
.pane .lead { margin: 0 0 18px; color: var(--muted); font-size: 13.5px; }

.search-box { display: flex; gap: 10px; margin-bottom: 18px; }
.search-box input {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: inherit;
}
.search-box input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.result-list { display: flex; flex-direction: column; gap: 8px; }
.result {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.result:hover { border-color: var(--accent); background: var(--accent-wash); }
.result-title { font-size: 14px; font-weight: 600; }
.result-when { font-size: 12px; color: var(--faint); font-weight: 400; margin-left: 8px; }
.result-snippet { margin: 4px 0 0; font-size: 12.5px; color: var(--muted); }
.result-mark { background: var(--mark); color: var(--mark-ink); border-radius: 2px; }
.pane-empty { padding: 14px 0; color: var(--faint); font-size: 13px; }

/* One row of the full list: the conversation on the left, and the one destructive thing on the
   right, which is why it is only offered here and not in the sidebar. */
.result-row { display: flex; align-items: center; gap: 8px; }
.result-row .result { flex: 1 1 auto; min-width: 0; }
.forget {
  flex: 0 0 auto;
  padding: 7px 11px;
  font-size: 12.5px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.forget:hover { border-color: var(--bad); color: var(--bad); }

/* A face and a column beside it. The person's turn is mirrored, so their bubble hugs the right
   edge - which is what says who said what before a word of it has been read. */
.turn {
  max-width: var(--column);
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.turn .column { flex: 1 1 auto; min-width: 0; }
/* The name is given the height of the face beside it, so the two read as one line rather than a
   label with a circle drifting below it. */
.turn .who { font-size: 12px; color: var(--muted); line-height: 28px; margin-bottom: 2px; }

.turn.mine { flex-direction: row-reverse; }
.turn.mine .column { display: flex; flex-direction: column; align-items: flex-end; }
.turn.mine .bubble { max-width: 80%; }

/* Lexi's picture, shown whole. The person's own face is the opposite case - a photograph, or two
   letters on a coloured disc - and both of those are circles everywhere else they are ever shown. */
.face {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  object-fit: contain;
}
.face.mine {
  border-radius: 50%;
  object-fit: cover;
  background: var(--mine);
  display: grid;
  place-items: center;
  color: var(--tint-ink);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.4px;
}
.face.mine.photo { display: block; }

.bubble {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 16px;
  overflow-wrap: anywhere;
}
.turn.mine .bubble { background: var(--mine); border-color: var(--line); }
/* Only the line that says what failed. The answer above it, when there is one, was really written
   and is really usable - colouring all of it red says the words are wrong rather than that the
   turn stopped short. */
.turn.failed .bubble.failed-note { border-color: var(--bad); color: var(--bad); }

.bubble > :first-child { margin-top: 0; }
.bubble > :last-child { margin-bottom: 0; }
.bubble p { margin: 0 0 10px; }
.bubble ul, .bubble ol { margin: 0 0 10px; padding-left: 22px; }
.bubble h2, .bubble h3 { font-size: 15px; margin: 14px 0 6px; }
.bubble a { color: var(--accent); }
.bubble code { background: var(--mine); border-radius: 4px; padding: 1px 5px; font-size: 13px; }
.bubble pre { background: var(--mine); border-radius: 6px; padding: 10px 12px; overflow-x: auto; }
.bubble pre code { background: none; padding: 0; }
.bubble table { border-collapse: collapse; margin: 0 0 10px; font-size: 14px; }
.bubble th, .bubble td { border: 1px solid var(--line); padding: 5px 9px; text-align: left; }

details.trail { margin-top: 10px; font-size: 12px; color: var(--muted); }
details.trail summary { cursor: pointer; }
details.trail ol { margin: 6px 0 0; padding-left: 20px; }

/* What ran, as a line each. The shape is borrowed from the desktop app: a small glyph, the name of
   what happened, then the argument in monospace running out to wherever the width ends. Everything
   after the title is secondary, so it stays quiet next to the answer it sits between. */
.stream { display: flex; flex-direction: column; gap: 10px; }

.act { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
.act-icon {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin-top: 3px;
  color: var(--faint);
}
.act-icon svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.2; }

.act-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  flex: 1 1 auto;
  background: none;
  border: 0;
  padding: 1px 0;
  font: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--muted);
  cursor: pointer;
  border-radius: 4px;
}
/* Chromium greys a disabled button's text. A row with nothing behind it is not doing any less than
   the others, it just has nothing to open. */
.act.bare .act-head { cursor: default; }
.act:not(.bare) .act-head:hover .act-title { text-decoration: underline; }

/* The name of what ran. It normally sets its own width and is left alone, but "normally" is not
   a guarantee: reasoning replayed from a transcript arrives as a paragraph, and a title that
   cannot give ground turns that into one line as wide as the sentence, with a scrollbar under
   the whole conversation to reach the end of it. */
.act-title {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The argument. It takes what room is left and gives up the rest, because a row that wraps to three
   lines is the panel this replaced. */
.act-detail {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 12px;
  color: var(--faint);
}
.act-count { flex: 0 0 auto; font-size: 12px; color: var(--faint); }
.act-count:not(:empty)::before { content: "\00b7"; margin-right: 6px; }

.act-body {
  grid-column: 2;
  margin: 4px 0 0 24px;
  max-height: 260px;
  overflow-y: auto;
  white-space: pre-wrap;
  /* A search argument or a URL has no spaces to break at, and one of those is wider than the
     column it sits in. Breaking mid-word is worse-looking than wrapping and better-looking
     than a scrollbar. */
  overflow-wrap: anywhere;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--faint);
  border-left: 2px solid var(--line);
  padding-left: 10px;
}
.act.open { flex-wrap: wrap; }
.act.open .act-body { flex: 1 1 100%; }

@keyframes act-spin { to { transform: rotate(360deg); } }
.act-icon .spin { animation: act-spin 1.4s linear infinite; }

/* The clock. Four dots going up and down under whatever is running, and a number beside them that
   actually moves - a turn can sit on one tool call for a minute, and a count going up is the
   difference between waiting and wondering whether it has died. */
.act.live { align-items: center; gap: 8px; }
.act.live .act-icon { margin-top: 0; display: flex; align-items: center; justify-content: center; }
.clock {
  font-size: 12px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.dots { display: grid; grid-template-columns: repeat(2, 3px); gap: 2px; }
.dots i {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  animation: act-bounce 1.1s ease-in-out infinite;
}
.dots i:nth-child(2) { animation-delay: .14s; }
.dots i:nth-child(3) { animation-delay: .28s; }
.dots i:nth-child(4) { animation-delay: .42s; }

@keyframes act-bounce {
  0%, 60%, 100% { opacity: .3; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-2px); }
}

@media (prefers-reduced-motion: reduce) {
  .act-icon .spin { animation: none; }
  .dots i { animation: none; opacity: .6; }
}

.bubble.note { background: none; border: 0; padding: 0; color: var(--ink); }
.bubble.stopped-note { font-size: 12.5px; color: var(--faint); }
.bubble.failed-note { color: var(--bad); }

/* Lexi's prose sits on the page rather than in a card. A turn is a run of rows and paragraphs
   reading top to bottom, and a border around only the last paragraph breaks that line. The question
   keeps its card, because that is what separates one exchange from the next. */
.turn.agent .bubble { background: none; border: 0; padding: 0; }
.turn.agent.failed .bubble.failed-note { color: var(--bad); }

.thinking .bubble { color: var(--muted); font-style: italic; }

/* ----------------------------------------------------------------- charts

   Adaptive Cards carry charts as `Chart.*` elements, which Teams draws natively and no browser
   does - not even the official Adaptive Cards renderer, because they are a Teams extension. So
   the card's data is read out here and the picture is drawn as SVG, which is also why it themes:
   every stroke and fill below is a variable rather than a colour, and a chart in Chilli Spice is
   the same chart in the palette it is being read in.

   The figures are written out underneath in a table that opens. A picture cannot be copied into
   a mail or read by a screen reader, and the numbers behind one are frequently the thing somebody
   actually wanted. */
.plot {
  margin: 2px 0 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  padding: 12px 14px 10px;
  min-width: 0;
}

.plot-title {
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 8px;
}

.plot-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* The grid is behind everything and must never compete with the data. */
.plot-grid line { stroke: var(--line); stroke-width: 1; }
.plot-axis line { stroke: color-mix(in srgb, var(--ink) 26%, var(--bg)); stroke-width: 1; }
.plot-svg text { fill: var(--faint); font: 11px "Segoe UI", system-ui, sans-serif; }
.plot-svg text.plot-axis-title { fill: var(--muted); font-size: 11.5px; }

/* A share written over its own slice sits on whichever of six colours that slice happens to be, so
   there is no one fill that reads on all of them. It is drawn in the page's own ink with the page's
   own background painted behind the letters instead: legible on a dark slice, on a light one, and
   in every palette, without knowing anything about which. */
.plot-svg text.plot-share {
  fill: var(--ink);
  stroke: var(--panel);
  stroke-width: 3px;
  paint-order: stroke;
  stroke-linejoin: round;
  font-size: 11.5px;
  font-weight: 600;
}

/* A line is a stroke, never a fill. The series classes below carry both, because a bar wants the
   fill and a line wants the stroke from the same declaration - so this has to out-rank them by
   naming the element as well as the class, or a line chart comes out as a filled blob. */
path.plot-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.plot-dot { stroke: var(--panel); stroke-width: 1.5; }
.plot-bar { stroke: none; }
.plot-slice { stroke: var(--panel); stroke-width: 1.5; }

/* Six series, six colours, and the modulo in the script means a seventh would reuse the first
   rather than come out unpainted. */
.s1 { stroke: var(--plot-1); fill: var(--plot-1); }
.s2 { stroke: var(--plot-2); fill: var(--plot-2); }
.s3 { stroke: var(--plot-3); fill: var(--plot-3); }
.s4 { stroke: var(--plot-4); fill: var(--plot-4); }
.s5 { stroke: var(--plot-5); fill: var(--plot-5); }
.s6 { stroke: var(--plot-6); fill: var(--plot-6); }

.plot-key {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--muted);
}
.plot-key span { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.plot-key i {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: 2px;
}
.plot-key i.k1 { background: var(--plot-1); }
.plot-key i.k2 { background: var(--plot-2); }
.plot-key i.k3 { background: var(--plot-3); }
.plot-key i.k4 { background: var(--plot-4); }
.plot-key i.k5 { background: var(--plot-5); }
.plot-key i.k6 { background: var(--plot-6); }

.plot-figures { margin: 8px 0 0; }
.plot-figures summary {
  font-size: 12px;
  color: var(--faint);
  cursor: pointer;
  list-style: none;
  padding: 2px 0;
}
.plot-figures summary::-webkit-details-marker { display: none; }
.plot-figures summary::before { content: "\25B8 "; }
.plot-figures[open] summary::before { content: "\25BE "; }
.plot-figures summary:hover { color: var(--ink); }
.plot-figures table {
  border-collapse: collapse;
  margin: 6px 0 0;
  font-size: 12.5px;
  display: block;
  max-width: 100%;
  overflow-x: auto;
}
.plot-figures th, .plot-figures td {
  border: 1px solid var(--line);
  padding: 3px 8px;
  text-align: left;
  white-space: nowrap;
}
.plot-figures th { color: var(--muted); font-weight: 600; }

/* A chart the card described but this page could not draw. Better than nothing where nothing is
   the alternative, and it carries the numbers, which were always the point. */
.plot-raw {
  font-size: 12.5px;
  color: var(--muted);
  margin: 0;
}

/* What the turn cost, under the answer. Deliberately quiet: it is a footnote on the answer, not
   part of it, and anything louder would compete with the sentence above it for attention. */
.spend {
  font-size: 11.5px;
  color: var(--faint);
  margin: 10px 0 0;
  font-variant-numeric: tabular-nums;
}

/* The one part that is allowed to be loud, and only when the context is close enough to the window
   that the next turn may have its older tool output summarised away. */
.spend .near {
  color: var(--ink);
  font-weight: 600;
}

#composer {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px 20px 10px;
  background: var(--panel);
  border-top: 1px solid var(--line);
}
.composer-inner {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  width: 100%;
  max-width: var(--column);
}
/* Box, tray and the row of controls travel together: what is about to be sent belongs inside the
   same outline as the words it is being sent with. */
.field {
  flex: 1 1 auto;
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
}
.field:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The bottom line of the box, and part of it. A strip rather than a floating row: the tint is what
   separates a control from the sentence above it, which is the whole reason these moved inside.
   Kept low on purpose - it is a footer to the box and not a toolbar - but not so low that the
   controls look squeezed against its edges. */
.field-tools {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  border-top: 1px solid var(--line);
  border-radius: 0 0 9px 9px;
  background: var(--field-bar);
}
.tool-gap { flex: 1 1 auto; }

.field-tools .tool {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 6px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.field-tools .tool.text { padding: 0 9px; }
.field-tools .tool:hover:not(:disabled) { background: var(--hover-deep); color: var(--ink); }
.field-tools .tool:disabled { opacity: .4; cursor: default; }
.field-tools #send { height: 26px; padding: 0 14px; font-size: 12.5px; }

/* Four lines to start with, and a grip to make it taller. Most questions here are longer than the
   one line this used to give: a paragraph of context about which area and which sprint is normal,
   and typing it into a slot that shows a sentence at a time is what makes people send it in
   pieces. It is resized by hand rather than by keystroke, so a box somebody dragged open stays
   open. */
#question {
  font: inherit;
  resize: vertical;
  min-height: 98px;
  max-height: 50vh;
  padding: 12px 12px 10px;
  border: 0;
  border-radius: 10px 10px 0 0;
  background: none;
  color: inherit;
}
#question:focus { outline: none; }
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.tray { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px 8px 0; }
/* display:flex outranks the hidden attribute's own display:none, so an empty tray would otherwise
   keep its padding and sit there as a blank strip. */
.tray[hidden] { display: none; }
.carried { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }

/* A thumbnail is the only label a screenshot has - a filename like "image (7).png" tells nobody
   which screenshot it is. Documents fall back to their name because they have nothing to show. */
.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--rail);
  overflow: hidden;
}
.chip.shot { width: 44px; height: 44px; }
.chip.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chip.doc { max-width: 180px; padding: 4px 8px; font-size: 12px; color: var(--muted); }
.chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-drop {
  position: absolute;
  top: 1px;
  right: 1px;
  width: 15px;
  height: 15px;
  padding: 0;
  line-height: 1;
  font-size: 12px;
  border: 0;
  border-radius: 50%;
  background: var(--veil);
  color: var(--veil-ink);
  cursor: pointer;
}
.chip.doc { padding-right: 20px; }

/* Send and Stop are the same button. While a turn runs it is the only control that is still live,
   so it says the one thing there is to do. */
#send.stop { background: var(--bad); border-color: var(--bad); }

body.dropping .field { border-color: var(--accent); background: var(--accent-wash); }

.colophon {
  width: 100%;
  max-width: var(--column);
  margin: 8px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 6px;
  font-size: 11.5px;
  color: var(--faint);
}
.colophon a { color: inherit; }

/* What Lexi is and what to say to it, shown once on arrival. It is a card rather than prose so it
   reads as the page introducing itself instead of as the first answer to a question nobody asked. */
.card {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px 18px;
  background: var(--rail);
}
.card h2, .card h3 { margin: 0 0 6px; font-size: 15px; }
.card ul { margin: 0; padding-left: 20px; }
.card li { margin: 0 0 4px; }
.card p { margin: 0 0 8px; }
.card .lead { color: var(--muted); }
.card code {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 5px;
  font-size: 12.5px;
}

/* ---------------------------------------------------------------- permission

   The gate on every write, drawn where the answer would have been. It is louder than the welcome
   card on purpose: this is the one thing on the page that does something irreversible, and it has
   a minute before it stops being able to. */
.approval { border-color: var(--accent-line); background: var(--accent-wash); }
.approval h3 { margin: 0 0 6px; font-size: 15px; }
.approval-lead { margin: 0 0 10px; color: var(--muted); font-size: 13px; }

.approval-list { margin: 0 0 10px; }
.approval-item {
  padding: 8px 0 0;
  border-top: 1px solid var(--accent-line);
}
.approval-what { margin: 0 0 2px; font-weight: 600; font-size: 13.5px; }
.approval-why { font-size: 13px; color: var(--muted); }
.approval-why p { margin: 0 0 4px; }
.approval-why pre { margin: 4px 0; }

/* The clock, which is the one thing on this card that is running out. It was a subtle grey line and
   went red at the end, which is the wrong way round: by the time it is red there are fifteen
   seconds left to notice it. The number is a red badge from the first second, and the whole line
   turns red once the last quarter of the window is gone. */
.approval-clock {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--muted);
}
.approval-count {
  flex: 0 0 auto;
  padding: 3px 10px;
  border: 1px solid var(--bad-line);
  border-radius: 999px;
  background: var(--bad-wash);
  color: var(--bad);
  font-size: 13px;
  font-weight: 700;
  /* So the badge does not change width as the digits do, which reads as the card twitching. */
  font-variant-numeric: tabular-nums;
}
.approval-clock.urgent { color: var(--bad); font-weight: 600; }
.approval-clock.urgent .approval-count {
  border-color: var(--bad);
  background: var(--bad);
  color: var(--bad-ink);
}

.approval-buttons { display: flex; gap: 8px; }
.approval-yes, .approval-no {
  padding: 7px 18px;
  font-size: 13.5px;
  border-radius: 6px;
  cursor: pointer;
}
.approval-yes { border: 1px solid var(--accent); background: var(--accent); color: var(--accent-ink); }
.approval-no { border: 1px solid var(--line); background: var(--panel); color: var(--ink); }
.approval-yes:hover { background: var(--accent-deep); }
.approval-no:hover { border-color: var(--bad); color: var(--bad); }

/* Pressed, or out of time. Both buttons stay on the page rather than vanishing - a card that
   loses its buttons reads as one that was never answered - but neither can be pressed again. */
.approval-yes:disabled, .approval-no:disabled {
  opacity: 0.45;
  cursor: default;
  background: var(--mine);
  border-color: var(--line);
  color: var(--muted);
}
.approval.spent { background: var(--rail); border-color: var(--line); }

/* ---------------------------------------------------------------- asking twice

   Deleting a conversation is the only thing here that cannot be undone, so it is the only thing
   that stops the page to ask. */
.veil {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--scrim);
}
.ask {
  width: min(420px, calc(100vw - 32px));
  padding: 20px 22px 16px;
  border-radius: 10px;
  background: var(--panel);
  box-shadow: 0 10px 40px var(--shadow);
}
.ask h3 { margin: 0 0 8px; font-size: 16px; }
.ask p { margin: 0 0 16px; font-size: 13.5px; color: var(--muted); }
.ask-buttons { display: flex; justify-content: flex-end; gap: 8px; }
.ask-no, .ask-yes {
  padding: 7px 16px;
  font-size: 13.5px;
  border-radius: 6px;
  cursor: pointer;
}
.ask-no { border: 1px solid var(--line); background: var(--panel); color: var(--ink); }
.ask-yes { border: 1px solid var(--bad); background: var(--bad); color: var(--bad-ink); }
.ask-no:hover { background: var(--mine); }
.ask-yes:hover { background: var(--bad-deep); }
