/* ============================================================================
   AVENLEO — WORKSPACE
   ----------------------------------------------------------------------------
   The conversation rail, the chat pane and the composer.

   This is the screen somebody sits in for an hour, so it is the one that has to
   be quietest. Three things changed and all three were noise:

     • The AI avatar and the send button were gradient tiles with a coloured
       glow around them. On a screen with twenty messages that is twenty glowing
       objects. Both are now flat.

     • Every message animated in on a transform. Streaming an answer meant a new
       layer per turn; the thread now simply appears.

     • The reward flourish pulsed with a text-shadow keyframe. It is a badge.

   What kept its motion: the thinking dots and the streaming caret, because both
   report that something is genuinely still happening.

   Depends on tokens/base/components/utilities. Media queries at the bottom.
   ========================================================================== */

:root {
  --ws-rail-w: 264px;
  --ws-measure: 720px;      /* the reading column for messages */
  --ws-composer-max: 760px;
}

.ws {
  display: flex;
  min-height: 0;
  height: calc(100dvh - var(--topbar-h));
  flex: 1;
}

/* ---------------------------------------------------------------------------
   CONVERSATION RAIL
   --------------------------------------------------------------------------- */
.ws-rail {
  width: var(--ws-rail-w);
  flex: none;
  display: flex; flex-direction: column;
  border-right: 1px solid var(--color-border);
  background: var(--color-surface);
  min-height: 0;
}
.ws-rail__head {
  padding: var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}
.ws-rail__search { position: relative; }
.ws-rail__search .icon-lead {
  position: absolute; left: var(--space-3); top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--color-text-faint); pointer-events: none;
}
.ws-rail__search input { padding-left: calc(var(--space-3) + 22px); }

.ws-rail__list { flex: 1; overflow-y: auto; padding: var(--space-2) var(--space-2) var(--space-6); display: flex; flex-direction: column; gap: 1px; }
.ws-rail__group {
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--color-text-faint);
  padding: var(--space-4) var(--space-2) var(--space-1);
}
.ws-rail__group:first-child { padding-top: var(--space-1); }

.conv {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 7px var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer; position: relative;
  color: var(--color-text-muted);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.conv:hover { background: var(--color-surface-2); color: var(--color-text); }
.conv[aria-current="true"] { background: var(--color-surface-3); }
.conv__icon { width: 14px; height: 14px; flex: none; color: var(--color-text-faint); }
.conv[aria-current="true"] .conv__icon { color: var(--color-text-muted); }
.conv__body { min-width: 0; flex: 1; }
.conv__title {
  display: block; font-size: var(--text-sm); font-weight: var(--weight-medium);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--color-text);
}
.conv[aria-current="true"] .conv__title { font-weight: var(--weight-semibold); }
.conv__meta { display: block; font-size: var(--text-2xs); color: var(--color-text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conv__kebab { opacity: 0; flex: none; transition: opacity var(--dur-fast); }
.conv:hover .conv__kebab, .conv:focus-within .conv__kebab { opacity: 1; }

.ws-rail__foot { padding: var(--space-2) var(--space-3); border-top: 1px solid var(--color-border); }
/* The bonus countdown, under the rewards figure it is measured against. Small
   and gold, because it is the same money the figure above it is — but not bold:
   the balance is the headline and this is the footnote to it. */
.ws-rail__bonus {
  margin-top: 4px;
  font-size: var(--text-2xs);
  line-height: var(--leading-snug);
  color: var(--color-accent-strong);
}
.ws-rail__bonus[hidden] { display: none; }

/* ---------------------------------------------------------------------------
   CHAT PANE
   --------------------------------------------------------------------------- */
.ws-chat { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; position: relative; }

.ws-chat__head {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  height: 48px;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg);
}
.ws-chat__title-wrap { min-width: 0; }
.ws-chat__title {
  font-family: var(--font-display); font-weight: var(--weight-semibold);
  font-size: var(--text-sm); letter-spacing: var(--tracking-snug);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ws-chat__sub { font-size: var(--text-2xs); color: var(--color-text-faint); display: flex; align-items: center; gap: var(--space-2); }

/* The session ledger is gone from the chat header — see the note in
   workspace.html. What is left is the offscreen holder for the ids ledger.js and
   composer.js still write to, so neither needs to know the display was removed.
   `hidden` already takes it out of the layout and the accessibility tree; this
   only guarantees it cannot be revealed by a stray style. */
.ws-ledger__hidden { display: none !important; }

/* When a reward settles, ledger.js marks the figure. A short colour lift,
   no scale and no text-shadow — it is a number changing, not an event. */
.earn-pulse { animation: earnLift var(--dur-slower) var(--ease-out); }
@keyframes earnLift { 0% { opacity: 0.45; } 100% { opacity: 1; } }

.ws-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; scroll-behavior: smooth; }
.ws-thread {
  max-width: var(--ws-measure); margin: 0 auto;
  padding: var(--space-6) var(--space-5) var(--space-8);
  display: flex; flex-direction: column; gap: var(--space-5);
}

/* ---------------------------------------------------------------------------
   MESSAGES
   --------------------------------------------------------------------------- */
.msg { display: flex; gap: var(--space-3); align-items: flex-start; }

.msg__avatar {
  width: 26px; height: 26px; border-radius: var(--radius-sm);
  flex: none; display: grid; place-items: center;
  font-size: 10px; font-weight: var(--weight-semibold);
}
.msg__avatar--ai { background: var(--color-primary); color: #fff; }
.msg__avatar--ai .icon { width: 15px; height: 15px; }
.msg__avatar--user { background: var(--color-surface-3); color: var(--color-text-muted); }

.msg__col { min-width: 0; flex: 1; }
.msg__name { font-size: var(--text-2xs); font-weight: var(--weight-semibold); color: var(--color-text-faint); margin-bottom: 6px; display: flex; align-items: center; gap: var(--space-2); }
.msg__bubble { border-radius: var(--radius-lg); }

/* The person's own turn: right-aligned, on a surface rather than a tint. It
   does not need a colour to be recognisable — its position says whose it is. */
.msg--user { flex-direction: row-reverse; }
.msg--user .msg__col { display: flex; flex-direction: column; align-items: flex-end; }
.msg--user .msg__name { flex-direction: row-reverse; }
.msg--user .msg__bubble {
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  padding: var(--space-3) var(--space-4);
  max-width: 100%;
  font-size: var(--text-md);
}
.msg--user .msg__bubble p { margin: 0; }

.msg--ai .msg__bubble { background: transparent; }
.msg--ai .prose { font-size: var(--text-md); }

.msg__meta { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-3); flex-wrap: wrap; }
.msg__cost { display: inline-flex; align-items: center; gap: 5px; font-size: var(--text-2xs); color: var(--color-text-faint); }
.msg__actions { display: flex; gap: 1px; margin-left: auto; opacity: 0; transition: opacity var(--dur-fast); }
.msg:hover .msg__actions, .msg:focus-within .msg__actions { opacity: 1; }

/* thinking indicator — motion that reports work in progress */
.msg__thinking { display: inline-flex; align-items: center; gap: var(--space-3); color: var(--color-text-subtle); font-size: var(--text-sm); padding: var(--space-1) 0; }
.dots { display: inline-flex; gap: 3px; }
.dots span { width: 4px; height: 4px; border-radius: var(--radius-full); background: var(--color-text-subtle); animation: dot 1.2s var(--ease-in-out) infinite; }
.dots span:nth-child(2) { animation-delay: .15s; }
.dots span:nth-child(3) { animation-delay: .3s; }
@keyframes dot { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

/* streaming caret */
.stream-caret {
  display: inline-block; width: 6px; height: 1em;
  transform: translateY(2px); margin-left: 1px; border-radius: 1px;
  background: var(--color-text-subtle);
  animation: caret 1s steps(2) infinite;
}
@keyframes caret { 50% { opacity: 0; } }

/* what an answer earned */
.reward-flash {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-2xs); font-weight: var(--weight-medium);
  color: var(--color-accent-strong);
  background: var(--color-accent-soft);
  border: 1px solid var(--color-accent-border);
  padding: 2px var(--space-2);
  border-radius: var(--radius-xs);
  font-variant-numeric: tabular-nums;
}
.reward-flash .icon { width: 11px; height: 11px; }

/* ---------------------------------------------------------------------------
   EMPTY STATE — a greeting, one line, and what a prompt pays

   The four opener cards are gone. They filled the screen with four decisions
   before the first one had been made, and the composer is right there. What is
   left is centred in the pane rather than sitting under the header, because
   three short elements pinned to the top read as a page that failed to load.
   --------------------------------------------------------------------------- */
.ws-thread--empty { min-height: 100%; display: flex; flex-direction: column; justify-content: center; }
.ws-empty {
  max-width: var(--ws-measure); margin: 0 auto;
  padding: var(--space-8) var(--space-5);
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
.ws-empty__mark { margin-bottom: var(--space-4); }
.ws-empty__greet {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tighter);
}

/* The cue. Not a control and not a suggestion — a sentence that changes, so
   the eye is told there is somewhere to type without another button to weigh.
   Motion is the whole point of it, so it is the one thing here that moves. */
.ws-cue {
  margin-top: var(--space-3);
  display: inline-flex; align-items: center; gap: var(--space-2);
  max-width: 100%;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.ws-cue__caret {
  width: 2px; height: 1.05em; flex: none;
  border-radius: 1px;
  background: var(--color-primary);
  animation: wsCueBlink 1.15s steps(1, end) infinite;
}
@keyframes wsCueBlink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* All three lines share one grid cell, so the row is as wide and as tall as
   the longest of them and nothing below it moves as they change. */
.ws-cue__rotator { display: grid; min-width: 0; text-align: left; }
.ws-cue__line {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateY(4px);
  animation: wsCueCycle 13.5s var(--ease-out) infinite;
}
.ws-cue__line:nth-child(2) { animation-delay: 4.5s; }
.ws-cue__line:nth-child(3) { animation-delay: 9s; }
@keyframes wsCueCycle {
  0%          { opacity: 0; transform: translateY(4px); }
  3%, 31%     { opacity: 1; transform: none; }
  34%, 100%   { opacity: 0; transform: translateY(-4px); }
}

/* base.css flattens every animation under reduced motion, which would leave
   the rotator on its last frame — invisible. Hold the first line instead. */
@media (prefers-reduced-motion: reduce) {
  .ws-cue__caret { animation: none; }
  .ws-cue__line { animation: none; transform: none; }
  .ws-cue__line:first-child { opacity: 1; }
}

.ws-empty__note { margin-top: var(--space-6); display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); color: var(--color-text-subtle); }
.ws-empty__note .coin { width: 15px; height: 15px; }

/* ---------------------------------------------------------------------------
   THE TASK BOARD
   ---------------------------------------------------------------------------
   The day's tasks, as the first thing on the workspace rather than a strip
   squeezed above the composer.

   The shape is a numbered list, and that is the whole design. A row of small
   side-scrolling cards asked somebody to read five things, work out that they
   were pressable, and then discover the two that were off the edge of the
   screen. A vertical list with a number, a sentence and one button per row is
   the shape of every instruction anybody has ever followed: there is exactly one
   thing to do on each line, and the numbers say what order to do them in.

   Nothing here is a new component. The button is .btn--primary.btn--sm, the bar
   is .progress, and the surfaces and borders are the same tokens the rest of the
   product draws cards with.
   --------------------------------------------------------------------------- */
.ws-board {
  max-width: var(--ws-measure);
  margin: 0 auto;
  padding: var(--space-2) var(--space-5) var(--space-8);
  width: 100%;
}
.ws-board[hidden] { display: none; }

/* ---- the head: what this is, how far along, and what to do ---- */

.ws-board__head { margin-bottom: var(--space-4); }

.ws-board__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tighter);
}

/* The one instruction on the screen. Deliberately a plain sentence at reading
   size — the old strip's heading was 10px uppercase tracking, which is a label
   for something you already understand. */
.ws-board__lede {
  margin-top: 2px;
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--color-text-muted);
}

.ws-board__progress {
  margin-top: var(--space-3);
  display: flex; align-items: center; gap: var(--space-3);
}
.ws-board__bar { flex: 1; min-width: 0; max-width: 240px; }
.ws-board__count {
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-subtle);
  white-space: nowrap;
}

/* ---- auto-continue ----
   The line that says the next task is about to send itself, and the two buttons
   that decide otherwise. Only ever on screen in `next` mode with work left, so
   it is never competing with the board for the top of an empty screen.

   Accent-bordered, because in this product that colour means money and this is
   the one line on the board that is about to earn some. No fill and no glow: it
   has to read as a status line with buttons on it, not as a dialog. */
.ws-board__auto {
  margin-top: var(--space-3);
  display: flex; align-items: center; gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.ws-board__auto[hidden] { display: none; }
.ws-board__auto[data-state="running"] { border-color: var(--color-accent-border); }

.ws-auto__text { flex: 1; min-width: 0; }

/* The digit. Tabular so 3 → 2 → 1 does not shuffle the sentence sideways, and
   accent because it is the only part of the line that is changing. */
.ws-auto__num {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-semibold);
  color: var(--color-accent-strong);
}

/* Both buttons the same size, deliberately. Stopping this is as easy as letting
   it run, which is the only thing that makes it acceptable to run at all. */
.ws-auto__btn {
  flex: none;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.ws-auto__btn:hover { border-color: var(--color-text-subtle); }
.ws-auto__btn--quiet {
  background: transparent;
  border-color: transparent;
  color: var(--color-text-subtle);
}
.ws-auto__btn--quiet:hover { color: var(--color-text); border-color: var(--color-border); }

/* ---- the list ---- */

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

.ws-task {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3);
  background: var(--color-surface-1);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.ws-task:hover { border-color: var(--color-border-strong); }

/* The number. Big enough to be the thing the eye lands on first, because it is
   the answer to "where do I start". Becomes a tick when the task is done. */
.ws-task__num {
  flex: none;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface-2);
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}
.ws-task__num .icon { width: 16px; height: 16px; }

.ws-task__body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }

/* The prompt itself, two lines of it. A card is a promise of what will be asked
   rather than a mystery button, and two lines is enough to keep that promise
   without turning the list into a wall. */
.ws-task__name {
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--color-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* What it pays. --aurum, because in this product that colour means earned money
   and appears nowhere else. */
.ws-task__pay {
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-strong);
}

.ws-task__go { flex: none; }

.ws-task__state {
  flex: none;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--color-success);
  padding: 0 var(--space-2);
}

/* Done. Dimmed rather than removed: a list that shortens as you work it gives
   you nothing to look back at, and the point of a checklist is the part you have
   already crossed off. */
.ws-task[data-done="true"] { background: transparent; }
.ws-task[data-done="true"] .ws-task__name { color: var(--color-text-subtle); }
.ws-task[data-done="true"] .ws-task__pay { color: var(--color-text-faint); }
.ws-task[data-done="true"] .ws-task__num {
  border-color: var(--color-success-border);
  background: var(--color-success-soft);
  color: var(--color-success);
}

/* The card the countdown is counting down to. A border and nothing else — the
   line above already says what is happening, and this only has to answer
   "which one" before the sentence is read. Only in `next`: on the full board
   nothing is armed, so nothing is singled out. */
.ws-board[data-mode="next"] .ws-task[data-next="true"] {
  border-color: var(--color-accent-border);
}

/* The whole board while a reply streams, or once the day is spent. The buttons
   are disabled in markup too — this is what explains why. */
.ws-board[data-locked="true"] .ws-task { opacity: .55; }

.ws-board__foot {
  margin-top: var(--space-4);
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}
.ws-board__foot a { color: var(--color-accent-strong); }
.ws-board__foot:empty { display: none; }

/* ---- the same board, under a finished answer ----
   tasks.js draws the board a second time once a reply has settled, listing only
   what is still undone, so nobody has to reload the page to reach the next task.
   It is the same component with the same cards — the difference is that it is no
   longer the subject of the screen, so it steps down a level: a rule to part it
   from the answer above, and a heading at body size rather than display size.

   The rule is drawn with a border rather than an <hr> because the board is the
   last element in the scroller, and a separator that belongs to the thing under
   it survives the board being hidden — which is what happens the moment the next
   task is pressed. */
.ws-board[data-mode="next"] {
  margin-top: var(--space-2);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-border-subtle);

  /* 120ms, which is about as short as an entrance can be and still register as
     one. Long enough that the cards read as having arrived rather than having
     always been there under the answer; short enough that nobody waits for it.

     Deliberately not a height or a slide: both animate layout, and this runs in
     the same frame the answer settles and the pane is scrolled, which is the
     worst possible moment to hand the compositor a reflow. Opacity and
     transform are the two properties that cost nothing. */
  animation: ws-board-in 120ms var(--ease-out) both;
}
@keyframes ws-board-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
/* Somebody who has asked for less motion gets the cards and none of the move.
   They still arrive in the same frame — the animation was never what made them
   fast. */
@media (prefers-reduced-motion: reduce) {
  .ws-board[data-mode="next"] { animation: none; }
}
.ws-board[data-mode="next"] .ws-board__title {
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  letter-spacing: var(--tracking-normal);
}
/* Nothing left to do: the head is the whole message, so the gap the list would
   have held is not left behind it. */
.ws-board[data-mode="next"] .ws-board__list:empty { display: none; }
.ws-board[data-mode="next"] .ws-board__head { margin-bottom: var(--space-3); }

/* ---- the greeting above it ----
   chat.js renders its empty state into #wsThread, which sits above the board.
   On its own that block fills the pane and centres itself, which is right when
   it is the only thing there and wrong when there is a board underneath. The
   class below is put on the scroller by tasks.js whenever the board is showing,
   and it turns the greeting back into a heading. */
.ws-scroll.is-board .ws-thread--empty { min-height: 0; justify-content: flex-start; }
.ws-scroll.is-board .ws-empty {
  /* `width: 100%` matters more than it looks. `.ws-empty` carries `margin: 0
     auto`, and an auto margin on a flex item's cross axis beats `align-items:
     stretch` — so the box is sized to its content, not to the pane. With the
     mark and the cue hidden below, that content is one short line, and the box
     collapsed to the width of the greeting and sat centred in the pane however
     the text inside it was aligned. Filling the measure is what puts the
     greeting on the same left edge as the board under it. */
  width: 100%;
  padding: var(--space-6) var(--space-5) var(--space-2);
  align-items: flex-start;
  text-align: left;
}
.ws-scroll.is-board .ws-empty__mark { display: none; }
.ws-scroll.is-board .ws-empty__greet { font-size: var(--text-xl); }
/* Both of these say what the board's own lede and pay figures already say, and
   saying it twice on the same screen is the clutter this rearrangement removes. */
.ws-scroll.is-board .ws-cue,
.ws-scroll.is-board .ws-empty__note { display: none; }

/* ---------------------------------------------------------------------------
   COMPOSER
   --------------------------------------------------------------------------- */
.composer-wrap {
  border-top: 1px solid var(--color-border);
  padding: var(--space-3) var(--space-5) var(--space-4);
  background: var(--color-bg);
}
.composer { max-width: var(--ws-composer-max); margin: 0 auto; }
.composer__box {
  display: flex; align-items: flex-end; gap: var(--space-2);
  background: var(--color-surface-1);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-xl);
  padding: 5px 5px 5px var(--space-3);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.composer__box:focus-within { border-color: var(--color-border-focus); box-shadow: 0 0 0 3px var(--color-focus-ring); }
.composer__textarea {
  flex: 1; border: 0; background: transparent; resize: none;
  color: var(--color-text);
  font-family: var(--font-sans); font-size: var(--text-md);
  line-height: var(--leading-normal);
  padding: 6px 0; max-height: 200px; min-height: 24px;
}
.composer__textarea:focus { outline: none; }
.composer__textarea::placeholder { color: var(--color-text-faint); }
.composer__tools { display: flex; align-items: center; gap: 1px; }
.composer__send {
  width: 30px; height: 30px; border-radius: var(--radius-md); flex: none;
  display: grid; place-items: center; border: 0; cursor: pointer;
  color: var(--color-primary-contrast);
  background: var(--color-primary);
  transition: background var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.composer__send:hover { background: var(--color-primary-hover); }
.composer__send:disabled { opacity: .35; cursor: not-allowed; }
.composer__send .icon { width: 15px; height: 15px; }
.composer__send--stop { background: var(--color-surface-3); color: var(--color-text); }
.composer__send--stop:hover { background: var(--color-border-strong); }

/* The keyboard hint that used to sit on the left is gone; what is left are the
   figures, and they belong on the right under the send button. */
.composer__foot {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--space-3); margin-top: 6px; padding: 0 var(--space-1);
  flex-wrap: wrap;
}
.composer__meta { display: inline-flex; align-items: center; gap: var(--space-3); }
.composer__cost { font-size: var(--text-2xs); color: var(--color-text-faint); }
.composer__cost b { color: var(--color-text-muted); font-weight: var(--weight-medium); }
.composer__cost .earn { color: var(--color-accent-strong); font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; }

.composer__count { font-size: var(--text-2xs); font-variant-numeric: tabular-nums; color: var(--color-text-faint); }
.composer__count.is-near { color: var(--color-warning); }
.composer__count.is-max { color: var(--color-danger); font-weight: var(--weight-semibold); }

.composer__quota { font-size: var(--text-2xs); color: var(--color-text-subtle); white-space: nowrap; font-variant-numeric: tabular-nums; }
.composer__quota.is-low { color: var(--color-warning); }
.composer__quota.is-spent { color: var(--color-danger); font-weight: var(--weight-medium); }

/* a request that failed: shown in place of an answer, never stored as one */
.msg__notice { font-size: var(--text-sm); color: var(--color-text-muted); border-left: 2px solid var(--color-danger); padding-left: var(--space-3); }

.model-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--text-2xs); font-weight: var(--weight-medium);
  color: var(--color-text-faint);
  padding: 1px var(--space-2); border-radius: var(--radius-xs);
}
.model-chip__dot { width: 5px; height: 5px; border-radius: var(--radius-full); background: var(--color-success); }

.ws-rail-toggle { display: none; }
.ws-rail-scrim { display: none; }

/* ===========================================================================
   RESPONSIVE
   =========================================================================== */
@media (max-width: 1024px) {
  .ws-rail-toggle { display: inline-grid; }
  .ws-rail {
    position: absolute; left: 0; top: 0; bottom: 0; z-index: var(--z-sticky);
    transform: translateX(-100%);
    transition: transform var(--dur-slow) var(--ease-out);
    box-shadow: var(--shadow-xl);
    width: min(300px, 86vw);
  }
  .ws-rail[data-open="true"] { transform: none; }
  .ws-rail-scrim {
    display: block; position: absolute; inset: 0;
    z-index: calc(var(--z-sticky) - 1);
    background: var(--color-scrim);
    opacity: 0; visibility: hidden;
    transition: opacity var(--dur-base), visibility var(--dur-base);
  }
  .ws-rail-scrim[data-open="true"] { opacity: 1; visibility: visible; }
  .ws { position: relative; }
}

@media (max-width: 640px) {
  /* The bottom bar is fixed, so the shell has to reserve its height — and the
     height rule that used to sit here never could. `.ws` is `flex: 1` inside
     `.main`, and flex-grow beats a height declaration outright: the pane was
     laid out a whole bar taller than the room it had, which is why the
     composer ended up underneath the bar. The padding is on the column that
     does the distributing, and the pane simply fills what is left. */
  .main { padding-bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom, 0px)); }
  .ws { height: auto; }
  .ws-chat__head { padding: var(--space-2) var(--space-3); }
  /* The one figure stays on a phone. It used to be four side by side, which is
     what the old `display: none` here was really solving. */
  .ws-thread { padding: var(--space-4) var(--space-4) var(--space-6); gap: var(--space-4); }
  .composer-wrap { padding: var(--space-2) var(--space-3) var(--space-3); }

  /* The board on a phone. The list already stacks, so this is only the gutters
     and giving the button a full-height target next to a two-line prompt. */
  .ws-board { padding: var(--space-2) var(--space-3) var(--space-6); }
  .ws-board__title { font-size: var(--text-base); }
  .ws-board__lede { font-size: var(--text-xs); }
  .ws-board__bar { max-width: none; }
  /* The countdown line on a phone: the sentence takes the width and the two
     buttons sit under it, rather than three things fighting over 340px and the
     Stop button wrapping away on its own. */
  .ws-board__auto { padding: var(--space-2); gap: var(--space-1) var(--space-2); }
  .ws-auto__text { flex: 1 0 100%; }
  .ws-auto__btn { padding: var(--space-1) var(--space-3); }
  .ws-task { padding: var(--space-2) var(--space-3); gap: var(--space-2); }
  .ws-task__num { width: 26px; height: 26px; font-size: var(--text-xs); }
  .ws-empty { padding: var(--space-6) var(--space-4); }
  .ws-empty__greet { font-size: var(--text-xl); }
  .ws-cue { font-size: var(--text-xs); }
  .msg__actions { opacity: 1; }        /* no hover on touch */
  .ws-chat__sub .model-chip { display: none; }
}

@media (max-width: 380px) {
  .ws-thread { padding-inline: var(--space-3); }
  .msg__avatar { width: 22px; height: 22px; }
}

@media (min-width: 1600px) {
  :root { --ws-measure: 780px; }
}

/* ---- the day, stopped by an operator ------------------------------------
   A locked board still draws its heading and its progress line — the figures
   are still true, and blanking the whole section would read as a broken page.
   What changes is that the list is empty and this says why. */
.ws-board__locked {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-warning-border);
  border-radius: var(--radius-md);
  background: var(--color-warning-soft);
  font-size: var(--text-sm); line-height: var(--leading-snug);
}
.ws-board__locked[hidden] { display: none; }

/* ---- Sunday: the standing rest day ---------------------------------------
   The one state on this screen with nothing to count. An admin's lock keeps
   the board's heading and its progress line because those figures are still
   true — a day was under way and somebody stopped it. Sunday is not that: no
   day has begun, so there is no bar at nought to draw and no "0 of 5 done"
   to print. The panel takes the whole board and answers the only question
   left, which is when it comes back.

   Colour means something here and is used once: the moon carries `--color-info`
   because nothing is wrong and nothing needs doing, which is exactly what the
   informational tone says everywhere else in the product. Everything else on
   the panel is ordinary text on an ordinary surface. */
.ws-board[data-rest="true"] .ws-board__lede,
.ws-board[data-rest="true"] .ws-board__progress,
.ws-board[data-rest="true"] .ws-board__auto,
.ws-board[data-rest="true"] .ws-board__foot,
.ws-board[data-rest="true"] .ws-board__locked { display: none; }

/* The heading goes quiet rather than away. `.ws-board` names itself with
   `aria-labelledby="wsBoardTitle"`, and a label pointing at a `display: none`
   node is no label at all — the section would announce as an unnamed group on
   the one day it has the most to say. So it keeps its place in the
   accessibility tree and gives up only its pixels, which the panel below has
   already taken over. */
.ws-board[data-rest="true"] .ws-board__head { margin-bottom: 0; }
.ws-board[data-rest="true"] .ws-board__title {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.ws-rest {
  max-width: 460px;
  margin: var(--space-4) auto var(--space-2);
  padding: var(--space-8) var(--space-6) var(--space-7);
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2xl);
  background: var(--color-surface-1);
  /* Entry, not decoration: this panel replaces a list somebody came here to
     press, and arriving rather than appearing is what says the screen changed
     on purpose. One move, 260ms, and never again. */
  animation: ws-rest-in 260ms var(--ease-out) both;
}
.ws-rest[hidden] { display: none; }

@keyframes ws-rest-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

.ws-rest__art {
  width: 132px; height: 88px;
  margin-bottom: var(--space-5);
  color: var(--color-info);
  overflow: visible;
}

/* The moon breathes: about five screen pixels, over seven seconds. Slower than
   the eye tracks as movement, which is the whole job on a panel that will sit
   there all day — it should read as the screen being alive rather than stuck,
   and never as something asking to be looked at.

   A pure translate, so it needs neither `transform-origin` nor `transform-box`
   — the two parts of CSS-on-SVG that old WebKit disagrees about, and every
   iPhone in this product is WebKit. The group is nested inside the one carrying
   the placement so this composes with that transform rather than replacing it,
   which is what an element with both would do. */
.ws-rest__moon {
  animation: ws-rest-drift 7s var(--ease-in-out) infinite;
}
@keyframes ws-rest-drift {
  0%, 100% { transform: translateY(1.2px); }
  50%      { transform: translateY(-1.2px); }
}

/* The stars, fainter than the moon and out of step with each other. Same
   duration on every one with a different delay: six animations that share a
   period never fall into a pulse the eye can count. */
.ws-rest__stars circle {
  color: var(--color-text-faint);
  fill: currentColor;
  animation: ws-rest-twinkle 4.4s var(--ease-in-out) infinite;
}
.ws-rest__stars circle:nth-child(1) { animation-delay: 0s; }
.ws-rest__stars circle:nth-child(2) { animation-delay: -3.1s; }
.ws-rest__stars circle:nth-child(3) { animation-delay: -1.4s; }
.ws-rest__stars circle:nth-child(4) { animation-delay: -2.2s; }
.ws-rest__stars circle:nth-child(5) { animation-delay: -0.7s; }
.ws-rest__stars circle:nth-child(6) { animation-delay: -3.8s; }
@keyframes ws-rest-twinkle {
  0%, 100% { opacity: .30; }
  50%      { opacity: .95; }
}

.ws-rest__title {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tighter);
  line-height: var(--leading-tight);
}

.ws-rest__body {
  margin-top: var(--space-3);
  max-width: 34ch;
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

/* The countdown. The only figure on the panel, so it gets the only box —
   and it is the server's boundary rendered in the reader's own clock, which
   is the half of "come back Monday" that is actually actionable. */
.ws-rest__clock {
  margin-top: var(--space-6);
  display: inline-flex; flex-wrap: wrap;
  align-items: baseline; justify-content: center;
  gap: var(--space-1) var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface-2);
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
}
.ws-rest__clock[hidden] { display: none; }
.ws-rest__left {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
  /* Tabular figures so a minute ticking over does not shuffle the words
     either side of it. */
  font-variant-numeric: tabular-nums;
}
.ws-rest__when { color: var(--color-text-faint); }

/* THE TRIAL PANEL.
   The same component with two ways out on it instead of a clock — see the
   markup note in workspace.html. Everything it needs is already on `.ws-rest`;
   this is the row of actions and the one thing the shared art must not do here,
   which is drift like a moon. */
.ws-rest__actions {
  margin-top: var(--space-5);
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  align-items: center; justify-content: center;
}
/* The emblem is a plain stroked icon at panel size rather than the moon, so it
   carries the stroke the sprite is drawn with and no fill. */
.ws-rest--trial .ws-rest__art {
  color: var(--color-primary);
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  margin-bottom: var(--space-4);
}

/* ---- THE TAKEOVER ------------------------------------------------------
   The free tasks are spent for good, and the panel gets the whole board — the
   same arrangement `data-rest` makes for a Sunday, and for a sharper version of
   the same reason. On a rest day the figures are hidden because there is no day
   to report on. Here they are hidden because they are a report on work already
   done and already paid for: "2 of 2 done" above two crossed-off cards is a
   member's own history, and leaving it up means scrolling past it to reach the
   only thing still actionable. So the history goes and the way forward takes
   its place.

   Driven by one attribute set in workspace/tasks.js, so what the board looks
   like in this state is described here and only here. */
.ws-board[data-trial="true"] .ws-board__lede,
.ws-board[data-trial="true"] .ws-board__progress,
.ws-board[data-trial="true"] .ws-board__auto,
.ws-board[data-trial="true"] .ws-board__foot,
.ws-board[data-trial="true"] .ws-board__locked,
.ws-board[data-trial="true"] .ws-board__list { display: none; }

/* The heading gives up its pixels and keeps its place in the accessibility
   tree — `.ws-board` is named by it through `aria-labelledby`, and a label
   pointing at a `display: none` node is no label at all. tasks.js rewrites it
   to "Free tasks used" for exactly this moment. Same treatment as the rest day;
   if you change one, change both. */
.ws-board[data-trial="true"] .ws-board__head { margin-bottom: 0; }
.ws-board[data-trial="true"] .ws-board__title {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- THE ENTRANCE -----------------------------------------------------
   One movement, in one direction, over 560ms end to end.

   It is staggered rather than simultaneous because the panel has an order to
   it — what happened, what it means, what to do — and letting those three land
   in that order is what makes the moment read as a conclusion rather than as a
   box appearing. Everything rises; nothing bounces, spins, scales past its
   final size or glows. A reward screen that oversells itself immediately after
   asking somebody for money reads as a sales pitch, and this product's design
   does not do that anywhere else either.

   `both` on every one of these matters: without it each part is briefly at its
   natural opacity before its delay elapses, which is a flash of the finished
   panel followed by it animating in. */
.ws-rest--trial { animation: ws-trial-rise 420ms var(--ease-out) both; }

.ws-rest--trial .ws-rest__art   { animation: ws-trial-emblem 420ms var(--ease-out) 60ms both; }
.ws-rest--trial .ws-rest__title { animation: ws-trial-part 380ms var(--ease-out) 140ms both; }
.ws-rest--trial .ws-rest__body  { animation: ws-trial-part 380ms var(--ease-out) 210ms both; }
.ws-rest--trial .ws-rest__actions { animation: ws-trial-part 380ms var(--ease-out) 290ms both; }

@keyframes ws-trial-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
/* The emblem is the one thing that scales, and only from .88 — enough to read
   as arriving, not enough to look like a notification. */
@keyframes ws-trial-emblem {
  from { opacity: 0; transform: scale(.88); }
  to   { opacity: 1; transform: none; }
}
@keyframes ws-trial-part {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 640px) {
  .ws-rest { padding: var(--space-6) var(--space-4); border-radius: var(--radius-xl); }
  .ws-rest__art { width: 108px; height: 72px; margin-bottom: var(--space-4); }
  .ws-rest__title { font-size: var(--text-lg); }
  .ws-rest__body { font-size: var(--text-sm); }
  /* Two full-width buttons rather than two narrow ones side by side: a deposit
     is the point of this panel and a thumb-sized target is how it gets pressed. */
  .ws-rest__actions { flex-direction: column; align-self: stretch; }
  .ws-rest__actions .btn { width: 100%; }
}

/* Nothing here is information, so nothing here moves for somebody who has
   asked the system for less motion. The panel still arrives, in place. */
@media (prefers-reduced-motion: reduce) {
  .ws-rest { animation: none; }
  /* The staggered entrance is the part that has to go, and it has to go by
     name: these carry `both`, so merely stopping them would leave every part
     stuck on its opening frame — an invisible panel. Setting `animation: none`
     drops the fill with it and each part sits at its natural state. */
  .ws-rest--trial,
  .ws-rest--trial .ws-rest__art,
  .ws-rest--trial .ws-rest__title,
  .ws-rest--trial .ws-rest__body,
  .ws-rest--trial .ws-rest__actions { animation: none; }
  .ws-rest__moon,
  .ws-rest__stars circle { animation: none; }
  .ws-rest__stars circle { opacity: .6; }
}
