/* BonBonBot Console: the assistant.
   ===================================================================
   Behaviour and the reasons for it are in src/scoop/. This file is
   only what it looks like.

   Three rules from docs/10 that a chat panel breaks by default:
     - No accent rail. Tone is the WHOLE border, the way a toast does it.
     - No gradient, no glow, no atmosphere. Depth is a step in lightness
       and a hairline.
     - Only the person gets a bubble. Bubbles on both sides is iMessage,
       and it halves the width of the half anybody re-reads. */

:root {
  --cbc-cop-width: var(--cb-width-xs);
  /* What the grip may drag between. Read out of here by grip.ts with
     getComputedStyle, so the two numbers stay in the token layer instead of
     sitting in JavaScript where check-tokens.ts cannot see them. */
  --cbc-cop-min: var(--cb-width-xs);
  --cbc-cop-max: var(--cb-width-lg);
  /* Focus mode. Not the panel's width with a bigger number: it is centred
     rather than docked, so it needs its own pair. */
  --cbc-cop-big: var(--cb-width-2xl);
  /* HOW TALL, and it was 768px, which is a mistake worth naming. Against a
     384px width that is exactly two to one: a column, not a panel, and on a
     laptop it stood in three quarters of the screen. Nothing needed the room.
     The transcript scrolls, so extra height buys a few more lines at rest and
     costs the impression that the assistant has taken the window over.

     640 against 384 is close to five to three, which is the proportion the rest
     of this console is built on, and it is the same token the grip already uses
     as its widest setting. */
  --cbc-cop-tall: min(
    var(--cb-width-lg),
    calc(100svh - var(--cbc-dock-lift) - var(--cbc-dock-inset))
  );
  /* 24px on 14px type. The console's own rhythm is 14/20, which is right for
     table rows and labels; generated prose is read rather than scanned, and
     14/24 is a reading rhythm. Both halves are tokens, so it is a decision
     rather than a magic number. */
  --cbc-cop-lead: var(--cb-type-base-line-height);
  --cbc-cop-gutter: var(--cb-space-3xl);
  --cbc-cop-tone: var(--cbc-line);
  /* The blur radius for the focus scrim. There is no blur scale in the token
     set, so the space scale is the only vocabulary; this exists because
     blur(4px) fails the token guard and blur(var(--cb-space-xs)) passes. */
  --cbc-cop-veil: var(--cb-space-xs);
  /* When the panel's contents begin arriving: before the shell's opacity has
     finished, so they start with it rather than after it. */
  --cbc-cop-settle: calc(var(--cb-motion-fast) * 0.8);
  --cbc-cop-step: calc(var(--cb-motion-fast) / 5);
}

/* ---- The launcher ----
   A scarlet squircle with a speech bubble in it, and the shape is the same
   quarter-width radius an app icon is drawn at. What changed is what is inside
   it: see launch-mark.ts for why the product's own logo came out. The short
   version is that the rail already draws the mark on every screen, so repeating
   it here told somebody the one thing they knew and nothing about what pressing
   it does.

   Never a gradient circle with a sparkle. That is the category's most
   reproduced signifier and it says nothing about this product. */
.cb-scoop-launch {
  position: fixed;
  z-index: var(--cbc-z-scoop);
  inset-block-end: var(--cbc-dock-inset);
  inset-inline-end: var(--cbc-dock-inset);
  display: grid;
  place-items: center;
  block-size: var(--cbc-dock-size);
  inline-size: var(--cbc-dock-size);
  padding: 0;
  border: 0;
  /* A quarter of the width, which is the proportion an app icon is drawn at.
     `2xl` was a third and read as a circle that had not committed. */
  border-radius: var(--cb-radius-xl);
  /* THE BUTTON IS THE SCARLET NOW. It used to be transparent with a scarlet
     image sitting in it, which is why the corner radius and the artwork's own
     corners had to be kept in agreement by hand. One painted surface has no
     seam to keep. */
  background: var(--cbc-accent);
  color: var(--cb-color-white);
  box-shadow: var(--cbc-shadow);
  cursor: pointer;
  /* Colour and shadow only. A control that grows under the pointer is the tell,
     and hover must never move layout (docs/09 §14). */
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    box-shadow var(--cb-motion-base) var(--cbc-spring);
}
/* On approach: a halo in the page's own ground, so the button separates from
   whatever is behind it, plus the accent's hover tone.

   NOT `--cbc-accent-quiet`, which was the first attempt and is nine percent
   alpha in the dark theme: an invisible ring, on the theme it was written for.
   The ground is the one value guaranteed to contrast with a scarlet square on
   both themes, because it is what the square is sitting on. */
.cb-scoop-launch:hover {
  background: var(--cbc-accent-hover);
  box-shadow:
    0 0 0 var(--cb-space-xs) var(--cbc-base),
    var(--cbc-shadow);
}
/* Focus is louder than hover on purpose: hover is a hint and focus is a
   keyboard user's only way of knowing where they are. */
.cb-scoop-launch:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 var(--cb-space-xxs) var(--cbc-base),
    0 0 0 var(--cb-space-sm) var(--cbc-accent),
    var(--cbc-shadow);
}
.cb-scoop-launch:active {
  transform: translateY(1px);
}

/* Fills the control, so the glyph inside it can be centred against the button
   rather than against its own contents. */
.cb-scoop-launch-face {
  display: grid;
  place-items: center;
  inline-size: 100%;
  block-size: 100%;
}
.cb-scoop-launch-face > * {
  grid-area: 1 / 1;
  transition:
    opacity var(--cb-motion-fast) var(--cb-motion-ease),
    transform var(--cb-motion-fast) var(--cbc-spring);
}
/* Two thirds of the button, which puts the drawn ink at about 60% of the tile:
   the proportion a platform app icon is drawn at. Bigger reads as a sticker
   filling its own square, smaller reads as a toolbar button that has floated
   loose, and this one has to be legible from the corner of an eye.

   No optical nudge here, unlike the send button, and it was measured before it
   was left alone. The ink centroid does sit under a pixel down and left of
   centre, because the bubble's tail hangs off that corner. Correcting for that
   would move the CIRCLE off centre to flatter a tail, and the circle and its
   three dots are what an eye actually locks onto: both are currently at 0.00
   in both axes. tests/cop-send.spec.ts photographs it at eight times so the
   next person can check rather than believe this. */
.cb-scoop-launch-glyph,
.cb-scoop-launch-shut {
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  color: var(--cb-color-white);
}
/* THE BUBBLE IS DRAWN A LITTLE HEAVIER than lucide's default. At this size on a
   saturated ground a two-unit stroke goes thin and the glyph loses its edge
   against the scarlet, which is the difference between a mark and a smudge. */
.cb-scoop-launch-glyph svg {
  stroke-width: 2.25;
}

/* ---- The three dots, thinking ----
   The bubble's own dots, not a second widget laid over it. A turn running with
   the panel shut is the one thing this corner can say that nothing else on the
   screen does, and the typing rhythm says it without a legend.

   Opacity and scale rather than a bob, because both are unitless: a translate
   here would be in SVG user units, which is a coordinate system with no token
   scale behind it, and a raw number in a stylesheet is what rule 13 is for.

   No reduced-motion block: console-theme.css already stops every animation in
   the console, and the dots are legible standing still. */
.cb-scoop-launch-glyph svg path:nth-of-type(n + 2) {
  transform-box: fill-box;
  transform-origin: center;
}
.cb-scoop-launch[data-busy] .cb-scoop-launch-glyph svg path:nth-of-type(n + 2) {
  animation: cbc-cop-think calc(var(--cb-motion-slow) * 2) var(--cb-motion-ease) infinite;
}
/* Out of step, the way the four-bar wave is. In step they read as an alarm
   rather than as work, which is the note waiting.ts makes about the bars. */
.cb-scoop-launch-glyph svg path:nth-of-type(3) {
  animation-delay: calc(var(--cb-motion-fast) / 2);
}
.cb-scoop-launch-glyph svg path:nth-of-type(4) {
  animation-delay: var(--cb-motion-fast);
}
@keyframes cbc-cop-think {
  0%,
  70%,
  100% {
    opacity: 0.4;
    scale: 0.7;
  }
  35% {
    opacity: 1;
    scale: 1.3;
  }
}

/* Something landed while you were away. A dot, not a count: the bell in the
   rail owns counts, and a number here would be a second one saying a different
   thing. */
.cb-scoop-launch[data-landed]::after {
  content: "";
  position: absolute;
  inset-block-start: var(--cb-space-xs);
  inset-inline-end: var(--cb-space-xs);
  inline-size: var(--cb-space-md);
  block-size: var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-good);
  border: 2px solid var(--cbc-raised);
  animation: cbc-pop var(--cb-motion-base) var(--cbc-spring) backwards;
}

/* Chevron down, not an X: the panel is going back down into the launcher. The
   delay means the bubble has gone before the chevron arrives, so the two never
   overlap into a smudge. */
.cb-scoop-launch-shut {
  opacity: 0;
  transform: scale(0.8);
}
.cb-scoop-launch[aria-expanded="true"] .cb-scoop-launch-glyph {
  opacity: 0;
  transform: scale(0.8);
}
.cb-scoop-launch[aria-expanded="true"] .cb-scoop-launch-shut {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--cb-motion-fast) * 0.4);
}

/* ---- The mark ----
   A rounded square, never a circle: the asset is a scarlet rounded square with
   white artwork inside, and circle-cropping clips it. A square beside a round
   photo is also the right signal: one is an app, one is a person. */
.cb-scoop-mark {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  border-radius: var(--cb-radius-sm);
  background: var(--cbc-accent);
  flex: none;
}
.cb-scoop-mark-fall {
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-on-accent);
}
.cb-scoop-mark-art {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  border-radius: var(--cb-radius-sm);
  opacity: 0;
}
/* Only once it has actually loaded. A 404 never fires load, so a missing file
   degrades to the letter rather than to a broken image. */
.cb-scoop-mark[data-loaded] .cb-scoop-mark-art {
  opacity: 1;
}
.cb-scoop-mark[data-loaded] .cb-scoop-mark-fall {
  opacity: 0;
}

/* ---- The panel ---- */
.cb-cop {
  position: fixed;
  z-index: var(--cbc-z-scoop);
  inset-inline-end: var(--cbc-dock-inset);
  inset-block-end: var(--cbc-dock-lift);
  inline-size: var(--cbc-cop-width);
  max-inline-size: calc(100vw - var(--cb-space-6xl));
  block-size: var(--cbc-cop-tall);
  display: grid;
  /* Only the middle track scrolls, so the header and the composer are always
     reachable. Lifted from .cb-notices, the console's existing corner panel. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-3xl);
  background: var(--cbc-raised);
  color: var(--cbc-ink);
  box-shadow: var(--cbc-shadow);
  overflow: hidden;
  /* Its own bottom-right corner, 12px above the launcher's top-right. That one
     fact is what ties the panel to the button it came from. */
  transform-origin: 100% 100%;
}
.cb-cop[hidden] {
  display: none;
}

/* Two animations on one element compose: one drives opacity, one the
   transform. Legible at 250ms, settled at 400ms, which is docs/10's "content
   settles into a container that has already arrived" applied to the container.

   THE POPOVER SIGNATURE, not a fourth entrance. This was cbc-cop-rise, which
   was cbc-pop retyped with 0.96 in place of 0.97: the same idea, growing out of
   the thing that opened it, under a second name in a second file. cbc-pop now
   carries the direction in --cbc-pop-from and its default is exactly the travel
   this wanted, so the panel uses the keyframe the rest of the console uses and
   the copy is gone.

   And `backwards`, not `both`: it ends where the panel already rests, so
   holding the final frame would pin the transform over anything the panel says
   about itself afterwards (console-theme.css has the two bugs that cost). */
.cb-cop[data-open] {
  animation:
    cbc-fade var(--cb-motion-base) var(--cb-motion-ease) backwards,
    cbc-pop var(--cb-motion-slow) var(--cbc-spring) backwards;
}
/* The close is its own animation, not the open reversed. Faster, eased rather
   than sprung (overshoot on an exit means it is briefly more present on its way
   out), and no scale, because scaling down is what makes a closing panel look
   sucked into a hole. .cb-toast sets the same in-at-base, out-at-fast rhythm. */
.cb-cop[data-closing] {
  pointer-events: none;
  animation: cbc-cop-out var(--cb-motion-fast) var(--cb-motion-ease) both;
}
@keyframes cbc-cop-out {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: translate3d(0, var(--cb-space-md), 0);
  }
}
/* Three steps and no more. Individual messages never stagger: the thread is
   one object arriving, not a list dealing itself out. */
.cb-cop[data-open] > * {
  animation: cbc-emerge var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: calc(var(--cbc-cop-settle) + var(--i, 0) * var(--cbc-cop-step));
}
.cb-scoop-thread {
  --i: 1;
}
.cb-scoop-foot {
  --i: 2;
}

/* ---- Header ---- */
.cb-scoop-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  block-size: var(--cb-space-6xl);
  padding-inline: var(--cb-space-xl);
  border-block-end: 1px solid var(--cbc-line);
}
/* A control, not a label. Pressing it widens the subject back to everything,
   which is the only direction it goes: narrowing is what opening a screen
   does. It shows its affordance on approach and not before (docs/09 rule 14),
   so a pinned subject reads as a statement until you reach for it. */
.cb-scoop-scope {
  border: 0;
  padding: var(--cb-space-xs) var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: none;
  font: inherit;
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-medium);
  color: var(--cbc-ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cbc-spring),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-scope:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
/* Nothing to widen, so nothing to press. */
.cb-scoop-scope[data-pinned="off"] {
  cursor: default;
}
.cb-scoop-scope[data-pinned="off"]:hover {
  background: none;
  color: var(--cbc-ink-muted);
}

/* The quiet line that says the subject moved under an open conversation.
   Centred and faint: it is punctuation in the thread, not a turn in it. */
.cb-scoop-turn-marker {
  margin-block: var(--cb-space-lg);
  text-align: center;
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
.cb-scoop-tools {
  display: flex;
  gap: var(--cb-space-xs);
  margin-inline-start: auto;
}
.cb-scoop-tool {
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-md);
  background: none;
  color: var(--cbc-ink-muted);
  cursor: pointer;
}
.cb-scoop-tool:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
.cb-scoop-tool[aria-disabled="true"] {
  color: var(--cbc-ink-faint);
  cursor: default;
}
.cb-scoop-tool[aria-disabled="true"]:hover {
  background: none;
}

/* ---- Thread ----
   overflow-anchor is the load-bearing line and it appears nowhere else in this
   console: Chrome's scroll anchoring fights an append-and-pin loop, and the
   result is the jitter people describe as jumpy streaming. */
.cb-scoop-thread {
  overflow-y: auto;
  overflow-anchor: none;
  overscroll-behavior: contain;
  padding: var(--cb-space-xl);
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-2xl);
}
.cb-scoop-turn {
  display: flex;
  gap: var(--cb-space-lg);
  contain: layout;
}
/* A column, so the files that went with a question stack under it. With one
   child this is pixel-identical to the row it replaced: align-items does the
   right-alignment that justify-content used to. */
.cb-scoop-turn[data-who="you"] {
  flex-direction: column;
  align-items: flex-end;
  /* Tighter than the turn gap, because a question and the files attached to it
     are one thing being said, not two. */
  gap: var(--cb-space-sm);
}
/* The person's turn is a bubble, in sunken. Not surface: in the light theme
   surface and raised are both white, so a surface bubble on a raised panel is
   invisible. Sunken is one rung down in both themes, so this is one rule with
   no theme fork. No tail, no pointer, no tightened corner. */
.cb-scoop-said {
  max-inline-size: 85%;
  padding: var(--cb-space-md) var(--cb-space-lg);
  border-radius: var(--cb-radius-2xl);
  background: var(--cbc-sunken);
  font-size: var(--cb-type-sm-size);
  line-height: var(--cbc-cop-lead);
}
/* The mark sits on the first line of the answer, so it carries the same top
   padding the body below has. Without it the two are a padding apart and the
   speaker appears to be labelling the line above the one they said. */
.cb-scoop-gutter {
  inline-size: var(--cbc-cop-gutter);
  flex: none;
  padding-block-start: var(--cb-space-lg);
}
/* THE BORDER IS ALWAYS THERE AND ONLY THE COLOUR CHANGES. The tone is the
   whole border rather than a bar down one edge (docs/10), and it used to be
   added along with the padding at the instant data-tone was set, so the
   paragraph shifted right and down at the exact moment something had gone
   wrong: the sentence somebody was reading moved as the reason to read it
   arrived. Drawn transparent at rest, it costs the same space in both states
   and the change is a colour, which is the one thing that can change without
   moving anything. */
.cb-scoop-turn-body {
  min-inline-size: 0;
  flex: 1 1 auto;
  border: 1px solid transparent;
  border-radius: var(--cb-radius-md);
  padding: var(--cb-space-lg);
  transition: border-color var(--cb-motion-base) var(--cb-motion-ease);
}

/* The assistant's own turn has no bubble: text directly on the panel. */
.cb-scoop-body {
  font-size: var(--cb-type-sm-size);
  line-height: var(--cbc-cop-lead);
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-lg);
  /* One line's worth, so the box never collapses under the indicator and then
     jumps when the first token lands. */
  min-block-size: var(--cbc-cop-lead);
}
.cb-scoop-tail:not([data-empty]) {
  display: contents;
}
.cb-scoop-p {
  margin: 0;
}
.cb-scoop-h {
  margin: 0;
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-semibold);
}
.cb-scoop-list {
  margin: 0;
  padding-inline-start: var(--cb-space-2xl);
  display: grid;
  gap: var(--cb-space-sm);
}
/* One level, indented rather than marked differently. A second bullet shape at
   this width is a distinction nobody reads. */
.cb-scoop-nested {
  margin-block-start: var(--cb-space-sm);
  padding-inline-start: var(--cb-space-xl);
}
.cb-scoop-tasks {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--cb-space-sm);
}
.cb-scoop-tasks li {
  display: flex;
  gap: var(--cb-space-md);
  align-items: baseline;
}
.cb-scoop-task-mark {
  color: var(--cbc-ink-faint);
}
.cb-scoop-tasks li[data-done] .cb-scoop-task-mark {
  color: var(--cbc-good-text);
}
.cb-scoop-rule {
  border: 0;
  border-block-start: 1px solid var(--cbc-line);
  margin: 0;
}

/* An indent and muted ink, never a coloured bar down the left edge. Every
   default markdown stylesheet draws the bar; docs/10 names it as the single
   most recognisable tell of a generated interface. */
.cb-scoop-quote {
  margin: 0;
  padding-inline-start: var(--cb-space-xl);
  color: var(--cbc-ink-muted);
}
.cb-scoop-quote p {
  margin: 0;
}

.cb-scoop-code {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  background: var(--cbc-sunken);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xs);
  padding: 0 var(--cb-space-xs);
}
.cb-scoop-codeblock {
  margin: 0;
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-md);
  background: var(--cbc-sunken);
  overflow: hidden;
}
.cb-scoop-codeblock figcaption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border-block-end: 1px solid var(--cbc-line);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
.cb-scoop-codeblock pre {
  margin: 0;
  padding: var(--cb-space-lg);
  overflow-x: auto;
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-sm-line-height);
}
.cb-scoop-copy {
  border: 0;
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  padding: 0;
  display: grid;
  place-items: center;
}
.cb-scoop-copy:hover {
  color: var(--cbc-ink);
}

/* Wide content scrolls inside its own container, so the transcript never
   scrolls sideways. */
.cb-scoop-scroll {
  overflow-x: auto;
}
.cb-scoop-table {
  border-collapse: collapse;
  font-size: var(--cb-type-xs-size);
  min-inline-size: 100%;
}
.cb-scoop-table th,
.cb-scoop-table td {
  text-align: start;
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border-block-end: 1px solid var(--cbc-line);
  white-space: nowrap;
}
.cb-scoop-table th {
  color: var(--cbc-ink-faint);
  font-weight: var(--cb-font-weight-medium);
}
.cb-scoop-table tbody tr:last-child td {
  border-block-end: 0;
}

.cb-scoop-cite,
.cb-scoop-route {
  color: var(--cbc-accent);
  text-decoration: none;
}
.cb-scoop-cite:hover,
.cb-scoop-route:hover {
  text-decoration: underline;
}

/* ---- Working ----
   The wave is .cb-live-bars reused, so "something is happening" looks the same
   here as it does in the rail. The sentence beside it is the load-bearing half
   and it is driven from real tool activity, never from a script. */
.cb-scoop-working {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  margin: 0;
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
}
.cb-scoop-wave {
  block-size: var(--cb-space-lg);
}

/* ---- The trace ----
   Kept after the answer arrives: it is the audit trail rendered as interface,
   and it becomes useful exactly when the answer is complete. */
.cb-scoop-trace {
  margin-block-start: var(--cb-space-lg);
  border: 1px solid var(--cbc-cop-tone);
  border-radius: var(--cb-radius-md);
  background: var(--cbc-sunken);
  font-size: var(--cb-type-xs-size);
}
.cb-scoop-trace[data-tone="warn"] {
  --cbc-cop-tone: var(--cbc-warn);
}
.cb-scoop-trace-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-md) var(--cb-space-lg);
  cursor: pointer;
  color: var(--cbc-ink-muted);
  list-style: none;
}
.cb-scoop-trace-head::-webkit-details-marker {
  display: none;
}
.cb-scoop-trace-what {
  flex: 1 1 auto;
  min-inline-size: 0;
}
.cb-scoop-trace-ms {
  color: var(--cbc-ink-faint);
  font-variant-numeric: tabular-nums;
}
.cb-scoop-trace-chev {
  transition: transform var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-scoop-trace[open] .cb-scoop-trace-chev {
  transform: rotate(180deg);
}
.cb-scoop-trace-list {
  margin: 0;
  padding: 0 var(--cb-space-lg) var(--cb-space-lg);
  list-style: none;
  display: grid;
  gap: var(--cb-space-md);
}
.cb-scoop-trace-list li {
  display: grid;
  gap: var(--cb-space-xxs);
}
.cb-scoop-trace-args {
  color: var(--cbc-ink-faint);
  font-family: var(--cb-font-mono);
}

/* ---- Meta ----
   The model and the cost sit back until asked for. The sample-data line does
   not: that one is a correctness disclosure, not paperwork. */
.cb-scoop-meta {
  display: flex;
  gap: var(--cb-space-lg);
  margin-block-start: var(--cb-space-md);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  opacity: 0;
  transition: opacity var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-scoop-turn:hover .cb-scoop-meta,
.cb-scoop-turn:focus-within .cb-scoop-meta {
  opacity: 1;
}
.cb-scoop-meta:has(.cb-scoop-sample) {
  opacity: 1;
}
.cb-scoop-sample {
  color: var(--cbc-warn-text);
}

.cb-scoop-turn[data-tone] .cb-scoop-turn-body {
  border-color: var(--cbc-cop-tone);
}
.cb-scoop-turn[data-tone="warn"] {
  --cbc-cop-tone: var(--cbc-warn);
}
.cb-scoop-turn[data-tone="poor"] {
  --cbc-cop-tone: var(--cbc-poor);
}
.cb-scoop-note {
  margin: 0;
  color: var(--cbc-ink-muted);
}

/* Appears on approach, and its height is reserved so nothing shifts when it
   does. Hover must never move layout. */
.cb-scoop-actions {
  block-size: var(--cb-space-3xl);
  display: flex;
  gap: var(--cb-space-xs);
  opacity: 0;
  transition: opacity var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-scoop-turn:hover .cb-scoop-actions,
.cb-scoop-turn:focus-within .cb-scoop-actions {
  opacity: 1;
}
.cb-scoop-act {
  border: 0;
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  padding: 0 var(--cb-space-xs);
}
.cb-scoop-act:hover {
  color: var(--cbc-ink);
}

/* ---- Empty state ----
   No greeting, no hero, no "Ask me anything". One line naming the scope and at
   most three questions the screen provokes but does not answer. */
/* ---- The opening ----
   This was one grey sentence, three grey pills, and four hundred and fifty
   pixels of nothing under them. A panel that opens onto emptiness reads as
   broken rather than as calm, and it is the first thing anybody ever sees.

   So it fills its own height: the mark and the greeting sit in the middle of
   the space, the offered questions sit under them, and the panel no longer has
   a hole in it. Grid rather than flex, because "the head is centred in
   whatever is left after the rows" is one line of grid and a calculation in
   anything else. */
/* ONE COMPOSITION, CENTRED, rather than two things pushed to opposite ends.
   `grid-template-rows: 1fr auto` with the head centred in the 1fr was the
   previous arrangement, and it put the intro in the middle of the upper half
   and the questions on the floor, with seventy pixels of nothing between them.
   Each half was placed correctly and the pair read as a hole, which is the
   fault the whole empty state was rewritten to remove in the first place.

   Centring the group closes it: the intro sits above its own questions, and
   the free space is shared evenly top and bottom instead of being collected in
   the middle where it shows. */
.cb-scoop-empty {
  block-size: 100%;
  display: grid;
  align-content: center;
  /* Wide enough that the questions are clearly a second thing, close enough
     that they are clearly this intro's questions. */
  gap: var(--cb-space-5xl);
  padding-block-end: var(--cb-space-lg);
}
.cb-scoop-empty-head {
  display: grid;
  justify-items: center;
  gap: var(--cb-space-md);
  text-align: center;
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: var(--cbc-landed);
}
/* The mark, at rest and larger than anywhere else. The panel has nothing to
   say at this moment, so it may as well have a face. A ring rather than a
   fill: docs/10 forbids a gradient circle and is right to, and a hairline is
   how depth is drawn everywhere else in this console. */
/* No ring. The brand tile IS the mark, and a grey circle drawn around it reads
   as a favicon somebody pasted in. It is given the panel's own shadow instead,
   so it sits ON the surface rather than in a hole cut out of it, and it is
   drawn at the size an app mark is actually drawn at. */
.cb-scoop-empty-mark {
  display: grid;
  place-items: center;
  margin-block-end: var(--cb-space-md);
}
.cb-scoop-empty-mark .cb-scoop-mark {
  inline-size: var(--cb-space-6xl);
  block-size: var(--cb-space-6xl);
  border-radius: var(--cb-radius-2xl);
  box-shadow: var(--cbc-shadow);
}
/* The one place in this panel that uses the type scale properly. Everything
   here was sm-regular, which is why it read as a settings screen. */
.cb-scoop-empty-title {
  margin: 0;
  font-size: var(--cb-type-lg-size);
  line-height: var(--cb-type-lg-line-height);
  letter-spacing: var(--cb-type-lg-letter-spacing);
  font-weight: var(--cb-font-weight-semibold);
  text-wrap: balance;
}
.cb-scoop-empty-line {
  margin: 0;
  max-inline-size: 34ch;
  font-size: var(--cb-type-xs-size);
  /* ITS OWN LEADING. This was `--cbc-cop-lead`, which is the BASE line height:
     24 pixels of leading on 12 pixel type, so the two lines of the one sentence
     here drifted a full line apart and the sentence stopped reading as a
     sentence. The composer wants the base lead because its text has to line up
     with 32px controls beside it. Nothing else does. */
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
  text-wrap: balance;
}
.cb-scoop-starters {
  display: grid;
  gap: var(--cb-space-md);
}
/* Rows, not pills. These are sentences, and a pill makes a sentence look like
   a tag: the old ones wrapped, sat at three different widths, and read as
   filters rather than as things to press. Staggered, so the opening arrives as
   one movement instead of three things landing at once. */
.cb-scoop-starter {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  inline-size: 100%;
  padding: var(--cb-space-lg) var(--cb-space-lg) var(--cb-space-lg) var(--cb-space-xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-sunken);
  color: var(--cbc-ink);
  font: inherit;
  font-size: var(--cb-type-sm-size);
  text-align: start;
  cursor: pointer;
  animation: cbc-rise var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: calc(var(--cbc-landed) + var(--i, 0) * var(--cb-motion-step));
  transition:
    border-color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-starter-text {
  flex: 1 1 auto;
}
/* The chevron leans in on approach. It never moves the row, which is the rule
   docs/09 §14 states: hover answers immediately and never shifts layout. */
.cb-scoop-starter-go {
  flex: none;
  color: var(--cbc-ink-faint);
  transition:
    transform var(--cb-motion-fast) var(--cbc-spring),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-starter:hover .cb-scoop-starter-go {
  transform: translateX(var(--cb-space-xs));
  color: var(--cbc-accent);
}
.cb-scoop-starter:hover {
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink);
}

/* ---- Composer ----
   The composer is an inset in the panel, which is literally what the ladder
   defines sunken as. */
.cb-scoop-foot {
  background: var(--cbc-sunken);
  border-block-start: 1px solid var(--cbc-line);
  padding: var(--cb-space-lg);
  display: grid;
  gap: var(--cb-space-md);
}
/* ---- The composer ----
   THE CENTRE OF GRAVITY, and it did not read as one. It was a thin rounded box
   the same weight as everything above it, with a hairline that turned bright
   red on focus, and the send button was a flat circle of accent sitting in it.
   Nothing about it said "this is the thing you use".

   What changed: it sits on `raised` against the sunken foot, so it lifts out of
   its own surroundings the way every other important element in this console
   does. More room inside. The focus state is a ring drawn OUTSIDE the border
   rather than a border that changes colour, so nothing shifts by a pixel and
   the box does not appear to flash. */
.cb-scoop-composer {
  position: relative;
  /* A grid rather than one flex row, because attachments sit ABOVE the field
     inside the same box: they belong to the question being written, so they
     live inside its border rather than floating above it. */
  display: grid;
  gap: var(--cb-space-md);
  padding: var(--cb-space-lg);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-3xl);
  background: var(--cbc-raised);
  box-shadow: var(--cbc-shadow);
  transition:
    border-color var(--cb-motion-fast) var(--cbc-spring),
    box-shadow var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-line {
  display: flex;
  align-items: flex-end;
  gap: var(--cb-space-sm);
}
/* THE FIELD GROWS TO MEET THE CONTROLS. It used to be the other way round: a
   negative bottom margin on the buttons, calc((24 - 32) / 2) = -4px, which
   under `align-items: flex-end` moves the MARGIN edge and therefore hung the
   three discs four pixels down into the composer's own padding. The glyph
   centres landed on the text, which is what it was for, but the send button
   then cleared the bottom border by 8px and the right border by 12px: the
   asymmetry had simply moved from one axis to the other.

   Four pixels of padding under the text does the same job from the other side.
   One line: field 28, row 32, text centre and control centres all at 16. Two
   lines: field 52, row 52, both at 36. The composer keeps 12px on all four
   sides and nothing overhangs anything. The cap below grows by the same four,
   so six lines is still six lines. */

/* ---- The slash menu ----
   Above the composer rather than below it, because the composer sits at the
   bottom of the panel and a list opening downwards would leave the screen. Same
   shape as the command palette on purpose: different verb, one visual language.
   Comes from the composer it belongs to, which is docs/09 rule 14 on where a
   popover is allowed to originate. */
.cb-scoop-menu {
  position: absolute;
  inset-inline: 0;
  bottom: calc(100% + var(--cb-space-md));
  /* Deep enough for a group and a half, so the menu reads as a list with more
     below rather than as two rows and a mystery. --cb-width-xxs is the same
     320px the hover note uses; there is no height scale that goes this far and
     inventing one for a single popover is worse than borrowing this. */
  max-block-size: var(--cb-width-xxs);
  overflow-y: auto;
  padding: var(--cb-space-sm);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-raised);
  box-shadow: var(--cbc-shadow);
  animation: cbc-emerge var(--cb-motion-fast) var(--cbc-spring) backwards;
}
.cb-scoop-menu[hidden] {
  display: none;
}
/* The heading carries the wave while the options are on their way, and there
   are no rows under it: nothing highlighted, nothing to click and nothing for
   Enter to take (command-menu.ts says what taking it used to do). */
.cb-scoop-menu-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-sm) var(--cb-space-md) var(--cb-space-xs);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
.cb-scoop-menu-row {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-md);
  inline-size: 100%;
  padding: var(--cb-space-sm) var(--cb-space-md);
  border: 0;
  border-radius: var(--cb-radius-md);
  background: none;
  color: var(--cbc-ink);
  font: inherit;
  font-size: var(--cb-type-sm-size);
  text-align: start;
  cursor: pointer;
}
/* Hover and keyboard land on the same state: two different highlights in one
   list is how somebody presses the row the mouse is over instead of the one
   the arrows are on. */
.cb-scoop-menu-row:hover,
.cb-scoop-menu-row[data-on] {
  background: var(--cbc-hover);
}
.cb-scoop-menu-note {
  margin-inline-start: auto;
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
  font-variant-numeric: tabular-nums;
}

/* ---- The command chip ----
   In front of the field, never inside it. The argument is an id off a list and
   must not be editable as characters. */
.cb-scoop-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-sm);
  flex: 0 0 auto;
  align-self: center;
  padding: var(--cb-space-xs) var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-hover);
  font-size: var(--cb-type-xs-size);
  white-space: nowrap;
}
.cb-scoop-chip[hidden] {
  display: none;
}
.cb-scoop-chip-name {
  color: var(--cbc-accent);
  font-weight: var(--cb-font-weight-medium);
}
/* Capped, because a long domain must not push the field and the send button off
   the end of a panel that cannot grow sideways. */
.cb-scoop-chip-arg {
  color: var(--cbc-ink-muted);
  max-inline-size: var(--cb-width-xxs);
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Drawn outside, never in the border itself. A border that changes colour on
   focus is a box that appears to flash; a ring around it is the box being
   picked out. Two rings, so it reads on both the light and the dark ground
   without either being a second colour. */
.cb-scoop-composer:focus-within {
  border-color: var(--cbc-line-strong);
  box-shadow:
    0 0 0 2px var(--cbc-accent-quiet),
    var(--cbc-shadow);
}
.cb-scoop-field {
  flex: 1 1 auto;
  min-inline-size: 0;
  /* NO LEFT PADDING OF ITS OWN. It had 6px on top of the composer's 12px, so
     the first letter of a question sat 18px from the border while the send
     button ended 12px from the other one. The box was lopsided by half a
     character and it is the sort of thing you feel before you can name. The
     composer's own padding is the only inset now, so both sides are one
     number.

     FOUR PIXELS UNDER IT, though, and that is load bearing. The row is
     `align-items: flex-end`, so a 24px field and a 32px disc share a bottom
     edge and their centres are four apart: the text sat four pixels below
     every glyph beside it. Padding under the text is what closes that, and it
     closes it from the field's side rather than by hanging the buttons down
     into the composer's own padding (see the note on .cb-scoop-line). */
  padding: 0 0 var(--cb-space-xs);
  border: 0;
  background: none;
  resize: none;
  /* SAID, NOT INHERITED. A textarea takes neither the page's font nor its ink
     from its parent: the browser gives it a form font and `color: FieldText`.
     This panel had neither declared, so the box somebody types their question
     into was rendering in a different typeface from every other word around
     it. console-forms.css carries the same note about the save bar, where the
     same omission once made a sentence white on white. */
  font: inherit;
  font-size: var(--cb-type-sm-size);
  line-height: var(--cbc-cop-lead);
  color: var(--cbc-ink);
  caret-color: var(--cbc-accent);
  /* Six lines, then it scrolls inside itself. Plus the bottom padding above,
     or the cap would quietly become five and a bit. */
  max-block-size: calc(var(--cbc-cop-lead) * 6 + var(--cb-space-xs));
  /* HIDDEN UNTIL THERE IS GENUINELY TOO MUCH, and this was a real, visible,
     embarrassing bug. A textarea overflows whenever its content is taller than
     its rows, and the PLACEHOLDER counts as content: an empty box, sitting
     there having been typed in by nobody, wrapped its own prompt onto a second
     line and drew a scrollbar down the side of itself. It read as a stray
     vertical rule inside the input.

     panel.ts's grow() turns this back to `auto` at the exact point the text
     really does pass the six-line cap, so the only scrollbar anybody ever sees
     is one that means something. */
  overflow-y: hidden;
}
.cb-scoop-field::placeholder {
  color: var(--cbc-ink-faint);
}
.cb-scoop-field:focus {
  outline: none;
}
/* Send and stop share one box. It never moves and never disappears; it changes
   what it is. */
.cb-scoop-send {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent);
  color: var(--cbc-on-accent);
  cursor: pointer;
}
/* Off is quiet, not grey-and-still-loud. A filled disc in `line-strong` reads
   as a button that is merely a different colour; an outline reads as one that
   is not ready. */
.cb-scoop-send[aria-disabled="true"] {
  background: none;
  border: 1px solid var(--cbc-line-strong);
  color: var(--cbc-ink-faint);
  cursor: default;
}
.cb-scoop-send:not([aria-disabled="true"]):hover {
  background: var(--cbc-accent-hover);
}
.cb-scoop-send:not([aria-disabled="true"]):active {
  transform: scale(0.94);
}
.cb-scoop-send {
  transition:
    background var(--cb-motion-fast) var(--cbc-spring),
    transform var(--cb-motion-fast) var(--cbc-spring);
}
/* ---- The paper plane, centred by its ink ----
   MEASURED, NOT GUESSED, because it was guessed twice and was wrong twice.

   Rendered at eight times and sampled, the glyph's BOUNDING BOX sits exactly on
   the disc's centre: dx 0.00, dy 0.00. That is why every attempt to "just
   centre it" changed nothing. Its INK sits +1.55px right and -1.56px up, which
   is the plane itself: it points to the top right, so its mass piles into that
   corner and the empty air is at the bottom left.

   So the correction is the negative of the measurement, and nothing else. Both
   values are under the token guard's hairline threshold, which is what that
   exemption is for: this is an optical nudge, not a spacing decision, and there
   is no token for "one and a half pixels of glyph balance". */
/* ONE GLYPH SIZE ACROSS THE ROW. The default `.cb-icon` is 20px and the clip
   and mic are 16, so the plane was drawn a quarter larger than the two controls
   beside it and read as the odd one out. A filled disc already carries more
   weight than an outline; making its glyph bigger as well says the same thing
   twice, and louder is not the same as clearer. The fill does the emphasising
   and all three glyphs are now one size. */
.cb-scoop-send .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
  transform: translate(-1.2px, 1.2px);
}
/* And the gap either side of the filled disc. `gap` gives all three the same
   number, but a filled disc carries more weight than an outline and needs more
   air beside it or it reads as crowding the mic. */
.cb-scoop-send {
  margin-inline-start: var(--cb-space-md);
}
/* ---- The keyboard legend ----
   FOUR CHIPS SAT UNDER THE INPUT PERMANENTLY, at the same visual weight as the
   thing somebody came here to type in. docs/10 names this exact failure:
   reassurance as furniture. They are the shortcuts for a box you are already
   looking at, so they are worth saying once, while you are in it, and worth
   nothing at all the rest of the time.

   Height is held either way, so revealing them cannot move the composer. */
.cb-scoop-keys {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-lg);
  min-block-size: var(--cb-space-2xl);
  /* The same inset as the first letter of a question, so the legend and the
     text it describes share one left edge. It was 8px against the composer's
     12px, which is the kind of five pixels that reads as carelessness without
     anybody being able to say why. */
  padding-inline: var(--cb-space-lg);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cb-motion-ease);
}
.cb-scoop-foot:focus-within .cb-scoop-keys {
  opacity: 1;
}
/* THE CHIP MUST NOT OUTWEIGH THE WORD. A bordered box around a glyph beside
   muted grey text puts the emphasis exactly backwards: the shortcut shouts and
   the thing it does whispers. The key gets the muted treatment and the word
   gets the ink, which is the way round somebody reads it. */
.cb-scoop-keys kbd {
  border-color: transparent;
  background: var(--cbc-hover);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
}
.cb-scoop-keys span {
  display: flex;
  align-items: center;
  gap: var(--cb-space-xs);
}

/* Announced, never shown. See panel.ts for why the transcript itself is not a
   live region. */
.cb-scoop-sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The panel takes the corner while it is open, so the toasts and the save sign
   step to its left. Sliding rather than jumping: a toast already on screen must
   not teleport. */
.cb-toast-stack,
.cb-saver {
  transition:
    inset-inline-end var(--cb-motion-base) var(--cb-motion-ease),
    inset-block-end var(--cb-motion-base) var(--cb-motion-ease);
}
body:has(.cb-cop[data-open]) .cb-toast-stack,
body:has(.cb-cop[data-open]) .cb-saver {
  inset-inline-end: calc(var(--cbc-dock-inset) + var(--cbc-cop-width) + var(--cb-space-lg));
  inset-block-end: var(--cbc-dock-inset);
}

@media (max-width: 768px) {
  /* mirrors tokens.breakpoint.md */
  .cb-cop {
    inline-size: calc(100vw - var(--cb-space-4xl));
    inset-inline-end: var(--cb-space-xl);
  }
}

/* A reduced-motion block lived here, empty, under a comment about a keyframe
   named cbc-wave that this file has never had: the panel's bars are
   `.cb-live-bars`, and console-theme.css already stops every animation in the
   console for anybody who has asked for less motion. An empty rule under a
   comment describing something that is not there is worse than nothing,
   because the next person reads the comment and believes it. */

/* ---- The two views that cover the thread ----
   Over it, not beside it: the panel is already narrow and a list that squeezed
   the transcript into a column would be worse at both jobs. `.cb-scoop-over` is
   the shell they share; each carries its own name as well, because two
   components under one class is how one of them silently gets the other's
   styling (see console-dossier.css on .cb-field). */
.cb-scoop-over {
  position: absolute;
  inset: 0;
  z-index: var(--cbc-z-rail);
  display: grid;
  grid-template-rows: auto 1fr;
  background: var(--cbc-raised);
  /* backwards, not both. cbc-emerge has no `to`, so its end state IS whatever
     this element rests at; holding the last frame forwards pins whatever the
     keyframe happened to leave and outranks every ordinary declaration on the
     element from then on, hover states included. */
  animation: cbc-emerge var(--cb-motion-fast) var(--cbc-spring) backwards;
}
.cb-scoop-over[hidden] {
  display: none;
}
.cb-scoop-history-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-lg) var(--cb-space-xl);
  border-block-end: 1px solid var(--cbc-line);
  color: var(--cbc-ink-faint);
}
.cb-scoop-history-head .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
/* The way out is always visible, unlike the per-row actions: a list that
   covers the panel and hides its own exit until you approach it is a trap. */
.cb-scoop-history-head .cb-scoop-history-act {
  opacity: 1;
}
.cb-scoop-history-search {
  flex: 1 1 auto;
  min-inline-size: 0;
  border: 0;
  background: none;
  color: var(--cbc-ink);
  font: inherit;
  font-size: var(--cb-type-sm-size);
}
.cb-scoop-history-search:focus {
  outline: none;
}
.cb-scoop-history-list {
  overflow-y: auto;
  padding: var(--cb-space-sm);
}
.cb-scoop-history-note {
  padding: var(--cb-space-xl);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
}
/* Under the rows rather than in place of them: a list being asked for again
   still holds results that are already correct, and taking them away to say so
   is the trade nobody wants (waiting.ts, and palette/rows.ts before it). */
.cb-scoop-waiting {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
}
.cb-scoop-history-row {
  display: flex;
  align-items: center;
  gap: var(--cb-space-xs);
  border-radius: var(--cb-radius-md);
}
.cb-scoop-history-row:hover {
  background: var(--cbc-hover);
}
.cb-scoop-history-row[data-on] {
  background: var(--cbc-hover);
}
.cb-scoop-history-open {
  flex: 1 1 auto;
  min-inline-size: 0;
  display: grid;
  gap: var(--cb-space-xxs);
  padding: var(--cb-space-md);
  border: 0;
  background: none;
  color: var(--cbc-ink);
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.cb-scoop-history-title {
  font-size: var(--cb-type-sm-size);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-scoop-history-when {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
/* The affordance arrives with the pointer, not before: two icons on every row
   of a list is a list of icons. */
.cb-scoop-history-act {
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-md);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-history-row:hover .cb-scoop-history-act,
.cb-scoop-history-act:focus-visible {
  opacity: 1;
}
.cb-scoop-history-act:hover {
  color: var(--cbc-ink);
  background: var(--cbc-surface);
}
.cb-scoop-history-act .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}

/* ---- A suggestion ----
   Reads as a thing to answer, not as a thing that happened. The title is the
   server's words; the model's reasoning is quieter underneath it, so the two
   can never be mistaken for each other. */
.cb-scoop-prop {
  display: grid;
  gap: var(--cb-space-sm);
  margin-block-start: var(--cb-space-lg);
  padding: var(--cb-space-lg);
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-sunken);
  /* It arrives in the middle of an answer that is still being written, into a
     column that has long since arrived, so it comes up the short 8px rather
     than the 20px a card travels: something small appearing inside something
     already settled. With no entrance at all it simply blinked into the middle
     of moving text, which reads as a rendering fault rather than as a question
     being asked. */
  animation: cbc-emerge var(--cb-motion-base) var(--cbc-spring) backwards;
}
.cb-scoop-prop-title {
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-semibold);
}
.cb-scoop-prop-state {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
}
.cb-scoop-prop-state[data-tone="warn"] {
  color: var(--cbc-warn-text);
}
/* The model's own sentence, set apart so it can never be read as the action. */
.cb-scoop-prop-why {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  font-style: italic;
}
/* The one line on a card that is about where a claim came from rather than
   what will happen. Warn-toned rather than faint, because it is the only thing
   here that should slow somebody down before they press. Not a rail and not a
   panel: one sentence, the colour of a caution, sitting where it is read last. */
.cb-scoop-prop-web {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-warn-text);
}
.cb-scoop-prop-acts {
  display: flex;
  gap: var(--cb-space-md);
  margin-block-start: var(--cb-space-sm);
}
.cb-scoop-prop-done {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
}

/* A remembered line, and what it has actually done. */
.cb-scoop-mem {
  flex: 1 1 auto;
  min-inline-size: 0;
  display: grid;
  gap: var(--cb-space-xxs);
  padding: var(--cb-space-md);
}
.cb-scoop-mem-text {
  font-size: var(--cb-type-sm-size);
}

/* ---- Attachments ----
   Inside the composer's border, above the field, because a file belongs to the
   question being written rather than to the panel. The row wraps: four chips at
   384px is two lines, and two lines of chips is better than four chips clipped.

   THE CHIP CARRIES ITS OWN FAILURE. There is no toast anywhere in this feature
   (attach-chips.ts says why): a file that could not be read says so where the
   file is, and taking the chip off takes the complaint with it. */
.cb-scoop-files {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-sm);
}
.cb-scoop-files[hidden] {
  display: none;
}
.cb-scoop-file {
  display: flex;
  align-items: center;
  gap: var(--cb-space-sm);
  max-inline-size: 100%;
  padding: var(--cb-space-xs) var(--cb-space-xs) var(--cb-space-xs) var(--cb-space-md);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-lg);
  background: var(--cbc-sunken);
  font-size: var(--cb-type-xs-size);
  animation: cbc-emerge var(--cb-motion-fast) var(--cbc-spring) backwards;
}
/* The whole chip takes the tone. Never a bar down one edge (docs/10). */
.cb-scoop-file[data-state="failed"] {
  border-color: var(--cbc-poor);
  color: var(--cbc-poor-text);
}
.cb-scoop-file[data-state="reading"] {
  color: var(--cbc-ink-faint);
}
.cb-scoop-file .cb-icon {
  flex: none;
  color: var(--cbc-ink-faint);
}
.cb-scoop-file[data-state="failed"] .cb-icon {
  color: inherit;
}
.cb-scoop-file-body {
  display: grid;
  min-inline-size: 0;
}
.cb-scoop-file-name {
  color: var(--cbc-ink);
  font-weight: var(--cb-font-weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-scoop-file[data-state="failed"] .cb-scoop-file-name {
  color: inherit;
}
/* The note wraps, because a refusal is a sentence and a sentence cut to one
   line with an ellipsis is a refusal nobody can act on. */
.cb-scoop-file-note {
  color: var(--cbc-ink-faint);
  max-inline-size: var(--cb-width-xxs);
}
.cb-scoop-file[data-state="failed"] .cb-scoop-file-note {
  color: inherit;
}
.cb-scoop-file-off {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  border: 0;
  border-radius: var(--cb-radius-md);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
}
.cb-scoop-file-off:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}

/* The paperclip. Same box as send so the row does not step when it appears,
   and quiet until approached: a control shows its affordance when you come to
   it and not before (docs/09 rule 14). */
.cb-scoop-clip {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-full);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-clip:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
/* The glyphs inside the two quiet controls, held one step under the arrow in
   the filled disc. All three boxes are the same size, which is right, but an
   outlined glyph at the same size as a filled one reads LOUDER, so at equal
   drawing they were three controls at three different weights. */
.cb-scoop-clip .cb-icon,
.cb-scoop-mic .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
/* And the wave that replaces the mic's mark while it is listening, or the
   control gets LIGHTER at the moment it becomes active: the mark is 16 and the
   shared wave is 12. Only here; the same wave is used by the working indicator
   and the launcher at its own size. */
.cb-scoop-mic .cb-scoop-wave {
  block-size: var(--cb-space-xl);
}

/* ---- Dragging a file onto the panel ----
   The panel itself is the target, because somebody dragging a file at a chat
   window aims at the chat window. Drawn as an inset outline rather than as an
   overlay: an overlay would cover the conversation the file is about, and the
   thing being answered is exactly what somebody checks while deciding whether
   this is the right window. */
.cb-cop[data-dropping]::after {
  content: "";
  position: absolute;
  inset: var(--cb-space-md);
  border: 2px dashed var(--cbc-accent);
  border-radius: var(--cb-radius-2xl);
  background: var(--cbc-accent-wash);
  pointer-events: none;
}

/* What went with a question, under it, in the same bubble. Never the content:
   the file was read once and thrown away, and a transcript holding what a
   transcript promised not to hold is the worst place to keep it. */
.cb-scoop-sent {
  /* After the question visually, before it in the DOM, so a screen reader
     hears what was asked before it hears what came with it. */
  order: 2;
  margin: 0;
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--cb-space-sm);
}
/* An outline rather than a fill, because the bubble above it is already the
   filled shape and two filled shapes in a column read as two messages. Muted
   ink, because what the question SAID matters more than what rode with it.

   Not `--cbc-on-tint`, which was the first attempt and was wrong: that token is
   an INK colour for use on a tinted background, so setting it as a background
   painted a dark pill and left the dark label invisible inside it. A screenshot
   of an empty black lozenge is what caught it. */
.cb-scoop-sent-one {
  padding: var(--cb-space-xxs) var(--cb-space-md);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-full);
  color: var(--cbc-ink-muted);
  font-size: var(--cb-type-xs-size);
}

/* ---- Dictation ----
   The mic is ALWAYS here, on or off. Leaving it out on Firefox would move the
   send button on one browser and not another, so the same screenshot would mean
   two different things (rule 15: turned off, never removed). */
.cb-scoop-mic {
  flex: none;
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-full);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-mic:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
.cb-scoop-mic[aria-disabled="true"] {
  color: var(--cbc-line-strong);
  cursor: default;
}
.cb-scoop-mic[aria-disabled="true"]:hover {
  background: none;
}
/* Listening: the glyph gives way to the console's own four-bar wave, in the
   same box. The bars carry the level, which is the same information a drawn
   waveform would carry at ten times the cost in a panel with no room for one. */
.cb-scoop-mic .cb-scoop-wave,
.cb-scoop-mic[data-on] .cb-icon {
  display: none;
}
.cb-scoop-mic[data-on] {
  background: var(--cbc-accent-wash);
  color: var(--cbc-accent);
}
.cb-scoop-mic[data-on] .cb-scoop-wave {
  display: flex;
}

/* What has been heard but not settled. Its own line under the field, never
   inside the textarea: provisional words in the box cannot be told apart from
   typed ones, so they get edited and then sent. */
.cb-scoop-ghost {
  margin: 0;
  /* No inset of its own. Both of these are siblings of the field inside the
     composer's 12px, so any padding here is pure offset from the words they
     are about: the ghost IS the sentence about to become the typed one, and
     the two have to start at the same x. */
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-sm-size);
  line-height: var(--cbc-cop-lead);
  font-style: italic;
}
.cb-scoop-ghost[hidden] {
  display: none;
}
/* Anything that went wrong with the microphone, in words, where the microphone
   is. Not a toast: this is about the control under the pointer. */
.cb-scoop-trouble {
  margin: 0;
  color: var(--cbc-poor-text);
  font-size: var(--cb-type-xs-size);
}
.cb-scoop-trouble[hidden] {
  display: none;
}

/* ---- Focus mode ----
   The same element, centred and larger, over a scrim. Not a second panel: a
   copy would mean two transcripts, two composers and two of every listener, and
   the conversation would have to be moved between them at the worst possible
   moment. One element, one data attribute, one FLIP (focus-mode.ts). */
.cb-cop[data-size="focus"] {
  inline-size: min(var(--cbc-cop-big), calc(100vw - var(--cb-space-7xl)));
  block-size: min(var(--cb-width-xl), calc(100svh - var(--cb-space-7xl)));
  inset-inline-end: 50%;
  inset-block-end: 50%;
  translate: 50% 50%;
  /* Grown from its own middle here, because it is no longer coming out of the
     launcher in the corner. */
  transform-origin: 50% 50%;
}
/* While the empty card is in flight. The horizontal factor between docked and
   focus is about 0.375, and text scaled by that and back smears badly enough to
   read as a rendering fault, so the contents wait and settle in on arrival. */
.cb-cop[data-flying] > * {
  opacity: 0;
}
.cb-cop:not([data-flying]) > * {
  transition: opacity var(--cb-motion-base) var(--cb-motion-ease);
}
.cb-scoop-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--cbc-z-scoop-scrim);
  background: var(--cbc-veil);
  /* THE ONE VEIL'S TREATMENT, which is now no blur at all: see --cbc-veil-blur
     in console-theme.css for the measurements. This dim sits over a live
     console for as long as somebody is talking to the assistant, which is the
     longest any of them is up and therefore the worst place to filter a whole
     viewport every time anything behind it moves. */
  backdrop-filter: var(--cbc-veil-blur);
}
/* NO FADE ON THE DIM. Same reason as the palette's and the dialog veil's: this
   element carries a backdrop-filter, and fading it re-composites a filtered
   copy of the whole viewport on every frame. The panel arrives on its own. */
.cb-scoop-scrim[hidden] {
  display: none;
}
/* The X exists over the scrim only, where there is no launcher to close
   through. focus-mode.ts argues the case against rule 15 in full. */
.cb-cop:not([data-size="focus"]) .cb-scoop-only-focus {
  display: none;
}
/* The launcher goes UNDER the scrim in focus mode, and this is a correctness
   fix rather than a polish one: the panel says `aria-modal` there, and a live
   button sitting on top of the dim is the one thing that makes that a lie.
   Dimmed and unreachable like everything else behind it, with the panel's own
   X and its minimise doing the two jobs it would have done. */
body[data-cop-focus] .cb-scoop-launch {
  z-index: calc(var(--cbc-z-scoop-scrim) - 1);
}

/* ---- The grip ----
   A separator rather than a button, which is what makes it reachable: screen
   readers already announce that role as a resizable divider, and the arrow keys
   are then behaviour people know rather than behaviour this panel invented.

   Quiet until approached (docs/09 rule 14). The hit area is wider than the line
   so it can actually be grabbed, and the line only appears on hover, focus or
   while it is being dragged. */
.cb-scoop-grip {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: var(--cb-space-md);
  cursor: ew-resize;
  z-index: 1;
}
/* A HANDLE, NOT A HAIRLINE. This drew a two pixel line down the whole 766px
   edge of the panel, and it is a tab stop: reaching it with the keyboard, or
   brushing the edge with the mouse, produced a bare vertical line with nothing
   around it to say what it was. "A button that appears like just a line" was
   the exact report, and it was right.

   A short pill at the middle of the edge is what a resize handle looks like
   everywhere else, it says "hold me" by its proportions alone, and it stops
   competing with the panel's own border for the whole height of the window. */
.cb-scoop-grip::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  /* Centred in the 8px hit area, and a token rather than a raw 3px: the guard
     only refuses 4 and above, so that one was passing on a technicality. */
  inset-inline-start: calc((var(--cb-space-md) - var(--cb-space-xxs)) / 2);
  inline-size: var(--cb-space-xxs);
  block-size: var(--cb-space-5xl);
  translate: 0 -50%;
  border-radius: var(--cb-radius-full);
  background: var(--cbc-line-strong);
  opacity: 0;
  transition:
    opacity var(--cb-motion-fast) var(--cbc-spring),
    block-size var(--cb-motion-fast) var(--cbc-spring);
}
/* Longer while it is actually being held, so the grab registers. */
.cb-scoop-grip[data-dragging]::after {
  block-size: var(--cb-space-7xl);
}
.cb-scoop-grip:hover::after,
.cb-scoop-grip:focus-visible::after,
.cb-scoop-grip[data-dragging]::after {
  opacity: 1;
}
/* The line IS the focus ring, rather than an outline around the hit area. The
   hit area is 8px wide and taller than the panel's rounded corner, so the
   default ring drew a bar that broke the corner and clipped the key legend
   underneath it. Same accent as the dragging state, because focused and being
   dragged are the same thing to a keyboard. */
.cb-scoop-grip:focus-visible {
  outline: none;
}
.cb-scoop-grip:focus-visible::after,
.cb-scoop-grip[data-dragging]::after {
  background: var(--cbc-accent);
}
/* Nothing to drag when the width is not the panel's to choose. */
.cb-cop[data-size="focus"] .cb-scoop-grip {
  display: none;
}

/* ---- A record on screen ----
   The assistant showing a scan or a business rather than describing it. Every
   value on it came from the server's own re-read (shared/scoop-cards.ts).

   IT MUST NOT LOOK LIKE A PROPOSAL, which may be sitting two inches below it.
   One is a thing you read and one is a decision you make, so this has no
   filled control anywhere on it: the whole card is the link, and the arrow is
   the only affordance. */
.cb-scoop-rec {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  margin-block-start: var(--cb-space-lg);
  padding: var(--cb-space-lg);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-sunken);
  color: inherit;
  text-decoration: none;
  transition:
    border-color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cbc-spring);
}
/* Answers immediately and never moves the layout (docs/09 rule 14). */
.cb-scoop-rec:hover {
  border-color: var(--cbc-line-strong);
  background: var(--cbc-hover);
}
/* A link with nothing measured on it. Quieter, or a sentence would carry the
   same visual authority as a record. */
.cb-scoop-rec[data-quiet] {
  background: none;
}
.cb-scoop-rec-mark {
  flex: none;
  color: var(--cbc-ink-faint);
}
.cb-scoop-rec-body {
  flex: 1 1 auto;
  min-inline-size: 0;
  display: grid;
  gap: var(--cb-space-xxs);
}
.cb-scoop-rec-name {
  margin: 0;
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-scoop-rec-sub {
  margin: 0;
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}
.cb-scoop-rec-facts {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--cb-space-sm) var(--cb-space-lg);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
}
.cb-scoop-rec-score {
  font-variant-numeric: tabular-nums;
  color: var(--cbc-ink);
}
/* The sentence that stands in for a missing number. Never a dash on its own:
   a blank covers a scan that has not started and one that could not reach the
   site, and they are different facts (views/nothing.ts). */
.cb-scoop-rec-why {
  color: var(--cbc-ink-faint);
  font-style: italic;
}
.cb-scoop-rec-state {
  font-weight: var(--cb-font-weight-medium);
}
.cb-scoop-rec-state[data-tone="good"] {
  color: var(--cbc-good-text);
}
.cb-scoop-rec-state[data-tone="warn"] {
  color: var(--cbc-warn-text);
}
.cb-scoop-rec-state[data-tone="poor"] {
  color: var(--cbc-poor-text);
}
.cb-scoop-rec-go {
  flex: none;
  color: var(--cbc-ink-faint);
}
.cb-scoop-rec:hover .cb-scoop-rec-go {
  color: var(--cbc-accent);
}

/* Figures on a card, and the period they are about. A number without its window
   can be read under the wrong heading, which is the one thing a figure card
   must not allow, so the heading is on the card rather than in the sentence
   above it. */
.cb-scoop-rec-figs {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-sm) var(--cb-space-xl);
}
.cb-scoop-rec-fig {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-sm);
}
.cb-scoop-rec-num {
  font-size: var(--cb-type-base-size);
  font-weight: var(--cb-font-weight-medium);
  font-variant-numeric: tabular-nums;
}
/* The board's own sparkline, reused. A second chart renderer is a second set of
   decisions about empty days. */
.cb-scoop-rec-spark {
  display: block;
  inline-size: 100%;
  color: var(--cbc-accent);
}

/* The mark in the header. Smaller than the one on the opening and drawn flat:
   a title bar states which surface this is, it does not introduce it. */
.cb-scoop-head-mark {
  display: grid;
  place-items: center;
  flex: none;
}
.cb-scoop-head-mark .cb-scoop-mark {
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
}

/* ---- What to ask next ----
   Under a landed answer, drawn as chips rather than as the opening's full
   rows: there IS something else on the screen here, and it is the answer
   somebody is reading. A footnote to it, never a second offer competing with
   it. They are removed the moment anything else is asked (ask.ts). */
.cb-scoop-next {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cb-space-sm);
  padding-inline-start: var(--cbc-cop-gutter);
}
.cb-scoop-next-one {
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-full);
  background: none;
  color: var(--cbc-ink-muted);
  font: inherit;
  font-size: var(--cb-type-xs-size);
  cursor: pointer;
  animation: cbc-rise var(--cb-motion-fast) var(--cbc-spring) backwards;
  animation-delay: calc(var(--i, 0) * var(--cb-motion-step));
  transition:
    border-color var(--cb-motion-fast) var(--cbc-spring),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-scoop-next-one:hover {
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink);
}

/* ---- Jump to the latest ----
   Shown only while the transcript has drifted away from the bottom. Sits above
   the composer rather than inside the transcript, so it never scrolls away
   from the thing it is offering to undo, and it is centred on the panel rather
   than tucked in a corner because it is about the whole column. */
.cb-scoop-latest {
  position: absolute;
  inset-block-end: calc(var(--cbc-dock-size) + var(--cb-space-6xl));
  inset-inline: 0;
  margin-inline: auto;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  display: grid;
  place-items: center;
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-raised);
  color: var(--cbc-ink);
  box-shadow: var(--cbc-shadow);
  cursor: pointer;
  z-index: 1;
  animation: cbc-rise var(--cb-motion-fast) var(--cbc-spring) backwards;
}
.cb-scoop-latest[hidden] {
  display: none;
}
.cb-scoop-latest:hover {
  border-color: var(--cbc-accent);
  color: var(--cbc-accent);
}

/* When it was said. In the action row, which is already revealed on hover and
   on focus-within, so it adds no furniture of its own: a permanent column of
   grey times down a conversation that happened two minutes ago says nothing.
   Pushed to the far end, away from the two controls, because it is a fact
   rather than a thing to press. */
.cb-scoop-when {
  margin-inline-start: auto;
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  font-variant-numeric: tabular-nums;
}
