/* ============================================================
   /chat/<slug> — a student chat, drawn as the app.

   Loaded after walkthrough.css, which is here for the --wt-*
   ink tokens and the shared page furniture. This sheet declares
   no palette of its own: the four things — paper, a ruled edge
   and a hard offset cast, mono for anything that labels rather
   than speaks, ink plus one accent — are already stated on
   --wt-*, and restating them with different numbers is the one
   thing docs/WIREFRAME-LANGUAGE.md forbids.

   ---- The three rules this page was rebuilt around ----

   1. THE CONVERSATION IS THE PAGE. It is not inside a box with a
      scrollbar of its own. A screen is wider than it is tall, and
      a chat in a fixed-height window on a landscape screen is a
      letterbox: the first version put twenty-five messages behind
      a nested scroller and the whole conversation could be — was
      — scrolled straight past. You scroll down and you read it.

   2. NOTHING ABOVE IT THAT IS NOT CARRYING ITS WEIGHT. There used
      to be four stacked bars over the chat: a browser frame with
      three dots and a URL, an app bar, a topic-and-progress
      strip, and a row with the perspective switch and the spine.
      On a 900px-tall screen that is a fifth of the height gone
      before a word of the conversation. They are now one sticky
      bar and a fourteen-pixel spine, and that bar stays with the
      reader — which is the only thing that earns any height at
      all.

   3. MONO LABELS, BODY SPEAKS (§4). Everything a student wrote is
      in the body face at a size you can read for twenty minutes;
      everything that labels it — the name over a bubble, a
      button, a column head, the gap markers — is mono.

   The chat is ONE screen and the quiz and survey are TWO, side by
   side. A chat is prose and needs a measure; a quiz and a survey
   are the same questions answered separately by two people who
   could not see each other's answers, and reading across the row
   is the whole content of them. The split turns up where
   comparison is the point and gets out of the way where reading
   is.
   ============================================================ */

/* ============================================================
   0. The one token this sheet declares
   ============================================================ */
/* Text on an accent fill. The light accent is a deep violet and takes paper
   on it at 5.5:1; the dark accent is a pale one, and paper on that is 2.9:1 —
   white lettering on lilac. So the fill's foreground is a token of its own:
   paper in light, ink in dark. Declared for both themes rather than given a
   literal fallback, so a missing token shows as no colour rather than as one
   that happens to work in one of them (§1.2). */
:root { --cp-on-accent: #fffefb; }
html.dark-mode { --cp-on-accent: #15151e; }

/* ============================================================
   1. The rig
   ============================================================ */
/* The site's own column, and not a pixel more: starting at the same left edge
   as /news, /research, /faq and /topics is what makes a page look like part of
   this site. Clip on the main, so nothing in here can widen the document
   however far a stroke overshoots. */
.site-main { overflow-x: clip; }

.cp { position: relative; margin-top: 1.4rem; }

/* ============================================================
   2. What the page says before the drawing starts
   ============================================================ */
.page-content .cp-eyebrow {
  font-family: "Roboto Mono", monospace;
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: 0 0 0.4rem;
}
/* The statement the two of them were rated on is not printed here. Guide says
   it in the first message of every chat, in the product's own words, thirty
   seconds after the reader starts reading — a plate above the page saying it
   first is a restatement, not an introduction. It is still on the card at
   /chats, and on the survey screen where each student rates it again. */

/* One quiet line between the title and the conversation. Not a bordered
   aside, not a callout — the original thread pages set this as plain small
   text and were right to. */
.page-content .cp-notice {
  font-size: 0.78rem;
  line-height: 1.65;
  color: var(--text-soft);
  margin: 0 0 1.1rem;
}
.page-content .cp-notice b { font-weight: 600; color: var(--wt-ink-strong); }

/* ============================================================
   3. The three screens, and which one is up
   ============================================================ */
/* All three, always, in the order a student meets them. The page is one
   scroll: you read the conversation, you reach the end of it, and the quiz
   and the survey are simply what is next. Nothing has to be pressed to see
   any of it — which is also exactly what a reader with no JavaScript gets,
   so there is no second layout to keep in step. */
.cp-act { margin-bottom: 3.2rem; }
.cp-act:last-of-type { margin-bottom: 0; }

/* The three parts, named. A number and a title, the way the walkthroughs
   number their screens — it is a document with three sections in it, and the
   reader is walking down it rather than pressing between panes.
   No leading article: WALKTHROUGH-STYLE.md §4.2, where twelve titles lost
   theirs. "Chat transcript", not "The chat". */
.page-content h2.cp-acthead {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  font-family: "Roboto Mono", monospace;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--text-strong);
  margin: 2.6rem 0 0.7rem;
  padding-top: 1.4rem;
  border-top: 1.5px dashed var(--wt-faint);
  scroll-margin-top: calc(var(--banner-h) + 1rem);
}
.page-content h2.cp-acthead.is-first { margin-top: 0.4rem; padding-top: 0; border-top: 0; }
.page-content h2.cp-acthead b {
  font-family: "Roboto Mono", monospace;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--wt-accent);
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   4. The one bar the chat keeps
   ============================================================ */
/* Sticky under the site banner, so the topic, whose screen you are on and how
   far through the conversation you are all stay with you. --banner-h is the
   chrome's own measure (64 / 48 / 76px) and is declared site-wide.
   It sticks only while the chat is on screen, because it is the chat's bar:
   sticky is bounded by its own containing block, and that is the section. */
.cp-topbar {
  position: sticky;
  top: var(--banner-h);
  z-index: 7;
  background: var(--wt-paper);
  border: 1.5px solid var(--wt-ink);
  border-radius: 5px 5px 0 0;
  box-shadow: 0 2px 0 var(--wt-cast);
  margin-bottom: 1.2rem;
}
.cp-topbar-in {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 0.85rem;
  font-family: "Roboto Mono", monospace;
}
.cp-brand { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.cp-mark { width: 1.15rem; height: 1.15rem; flex: none; color: var(--wt-accent); }
.cp-mark svg { display: block; width: 100%; height: 100%; }
.page-content .cp-brand b {
  font-family: "Roboto Mono", monospace;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Whose screen you are on, and the way to the other one. The avatar is the
   product's own; the button around it is this page's. */
.cp-swap {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border: 1.5px solid var(--wt-faint);
  border-radius: 999px;
  background: var(--wt-paper);
  padding: 0.2rem 0.6rem 0.2rem 0.2rem;
  font-family: "Roboto Mono", monospace;
  font-size: 0.7rem;
  color: var(--text-soft);
  cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease;
}
.cp-swap:hover { border-color: var(--wt-ink-strong); color: var(--wt-ink-strong); }
.cp-swap:focus-visible { outline: 2px solid var(--wt-accent); outline-offset: 2px; }
.page-content .cp-swap b {
  font-family: "Roboto Mono", monospace;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
.cp-avatar {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  border: 1.5px solid var(--wt-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Roboto Mono", monospace;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
  background: var(--wt-accent-soft);
  flex: none;
}
.cp-glyph { font-size: 0.9em; line-height: 1; }

/* ---- the narration ----
   Every message in every thread has a recorded start time, so the transcript
   follows the audio rather than just playing beside it: the message being
   spoken is marked, and the page keeps up with it.

   Ruled, not drawn: a drawn box is a thing you read and a ruled one is a thing
   you press (§2), and a control whose outline is regenerated every time its
   fill moves is not a control anybody wants. */
.cp-audio {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: "Roboto Mono", monospace;
}
.cp-ap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.7rem;
  height: 1.7rem;
  padding: 0 0.35rem;
  border: 1.5px solid var(--wt-faint);
  border-radius: 4px;
  background: var(--wt-paper);
  font-family: "Roboto Mono", monospace;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1;
  color: var(--wt-ink-strong);
  cursor: pointer;
  transition: border-color 0.14s ease, background-color 0.14s ease;
}
.cp-ap:hover { border-color: var(--wt-ink-strong); }
.cp-ap:focus-visible { outline: 2px solid var(--wt-accent); outline-offset: 2px; }
/* The one control here that starts something takes the accent fill. */
.cp-ap-play { border-color: var(--wt-accent); background: var(--wt-accent); }
.cp-ap-play:hover { border-color: var(--wt-accent); }
/* Drawn in CSS rather than set as a character: ▶ and ⏸ render as full-colour
   emoji on some platforms and as tofu on others, and neither is the ink. */
.cp-ap-icon {
  display: block;
  width: 0;
  height: 0;
  margin-left: 2px;
  border-top: 0.34rem solid transparent;
  border-bottom: 0.34rem solid transparent;
  border-left: 0.52rem solid var(--cp-on-accent);
}
.cp-audio.is-playing .cp-ap-icon {
  width: 0.5rem;
  height: 0.62rem;
  margin-left: 0;
  border: 0;
  background: linear-gradient(
    to right,
    var(--cp-on-accent) 0 36%,
    transparent 36% 64%,
    var(--cp-on-accent) 64% 100%
  );
}
.cp-ap-time {
  font-size: 0.64rem;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cp-ap-track {
  width: clamp(4rem, 12vw, 9rem);
  height: 1.7rem;
  display: flex;
  align-items: center;
  cursor: pointer;
  touch-action: none;
}
.cp-ap-track::before {
  content: "";
  flex: 1;
  height: 3px;
  border-radius: 2px;
  background: var(--wt-faint);
}
.cp-ap-track { position: relative; }
.cp-ap-fill {
  position: absolute;
  left: 0;
  top: 50%;
  height: 3px;
  margin-top: -1.5px;
  width: var(--pct, 0%);
  border-radius: 2px;
  background: var(--wt-accent);
  pointer-events: none;
}
.cp-ap-track:focus-visible { outline: 2px solid var(--wt-accent); outline-offset: 2px; border-radius: 3px; }
.cp-ap-speed { min-width: 2.1rem; }

/* The message being spoken. An accent rule down its edge and the paper
   lifted — the same two marks Guide's own messages use, because this is the
   same idea: "this one, now". Nothing moves and nothing is redrawn. */
.cp-msg.is-speaking .cp-bubble {
  box-shadow: inset 3px 0 0 var(--wt-accent);
  padding-left: 1.15rem;
}
.cp[data-me="left"] .cp-msg[data-who="left"].is-speaking .cp-bubble,
.cp[data-me="right"] .cp-msg[data-who="right"].is-speaking .cp-bubble {
  box-shadow: inset -3px 0 0 var(--wt-accent);
  padding-left: 0.9rem;
  padding-right: 1.15rem;
}
/* Once the narration has been started, a message is a place in it. */
.cp[data-seekable] .cp-msg { cursor: pointer; }

/* ---- the spine, as the bar's own bottom edge ----
   Every message is a tick: the student who disagrees below the axis, the one
   who agrees above it, Guide as a diamond on the axis itself. Position
   carries who — colouring one student one way and the other another would be
   the mistake /topics avoids by drawing agreement and disagreement in the
   same ink. The accent is spent on where you are and nothing else. */
.cp-spine {
  position: relative;
  height: 14px;
  border-top: 1.5px dashed var(--wt-faint);
  cursor: pointer;
  touch-action: none;
}
.cp-spine:focus-visible { outline: 2px solid var(--wt-accent); outline-offset: -2px; }
.cp-axis {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: var(--wt-faint);
  opacity: 0.7;
}
.cp-ticks { position: absolute; inset: 0; }
.cp-tick {
  position: absolute;
  width: 2px;
  margin-left: -1px;
  background: var(--wt-ink);
  border-radius: 1px;
  opacity: 0.45;
}
.cp-tick.is-right { bottom: 50%; height: 5px; }
.cp-tick.is-left { top: 50%; height: 5px; }
.cp-tick.is-guide {
  top: 50%;
  left: var(--x);
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  background: var(--wt-ink-strong);
  border-radius: 0;
  transform: rotate(45deg);
  opacity: 0.8;
}
.cp-tick.is-seen { opacity: 1; }
/* How much of the conversation is on screen, and where. */
.cp-band {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--x, 0%);
  width: var(--w, 15%);
  min-width: 4px;
  background: var(--wt-accent-soft);
  box-shadow: inset 0 -1.5px 0 var(--wt-accent);
  pointer-events: none;
}
html:not(.cp-js) .cp-topbar { position: static; }
html:not(.cp-js) .cp-spine, html:not(.cp-js) .cp-swap { display: none; }

/* ============================================================
   5. The conversation
   ============================================================ */
.cp-thread {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  /* The conversation sits in the middle of the column rather than along its
     left edge. Left to fill the whole width, a run of messages from one
     person stacked up one side and left half the page empty, which reads as a
     layout fault rather than as a chat.
     The 8px floor is the room the strokes need: they overshoot their bubble
     by seven pixels, and with no padding at all the leftmost of them fell
     outside the column and was clipped. */
  padding: 0 max(8px, calc((100% - 58rem) / 2));
}

.cp-msg {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* A measure, not a proportion: about seventy characters of the body face,
     which is what a paragraph wants. */
  max-width: min(34rem, 88%);
  /* The sticky bar is 3.4rem tall; a message jumped to must not land under
     it. */
  scroll-margin-top: calc(var(--banner-h) + 4.5rem);
}
/* Guide is indented from the left and carries an accent bar down its edge,
   which is what the product does with it — it is neither of the two of them,
   and the shape says so before the name does. The bar is an inset shadow
   rather than a border, because a drawn box has its borders taken off. */
.cp-msg[data-who="guide"] {
  max-width: min(40rem, 96%);
  margin-left: clamp(0px, 4%, 2.5rem);
}
.cp-msg[data-who="guide"] .cp-bubble {
  box-shadow: inset 3px 0 0 var(--wt-accent);
  padding-left: 1.15rem;
}

/* Whose screen you are on decides which side a student's own messages sit.
   That single comparison is the whole of the split — and it is a class on the
   rig rather than two copies of the thread, so the conversation exists once. */
.cp[data-me="left"] .cp-msg[data-who="left"],
.cp[data-me="right"] .cp-msg[data-who="right"] { align-self: flex-end; align-items: flex-end; }
/* Your own name is not printed over your own messages, in the product or
   here. */
.cp[data-me="left"] .cp-msg[data-who="left"] .cp-who,
.cp[data-me="right"] .cp-msg[data-who="right"] .cp-who { display: none; }
.cp[data-me="left"] .cp-msg[data-who="left"] .cp-bubble,
.cp[data-me="right"] .cp-msg[data-who="right"] .cp-bubble {
  background: var(--wt-accent-soft);
  color: var(--wt-ink-strong);
}

.page-content .cp-who {
  font-family: "Roboto Mono", monospace;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--text-soft);
  margin: 0 0 0.25rem 0.35rem;
}

/* A chat bubble is a thing somebody said, so it is set in the body face. */
.cp-bubble {
  position: relative;
  border: 1.5px solid var(--wt-faint);
  border-radius: 6px;
  padding: 0.7rem 0.9rem;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.95rem;
  line-height: 1.68;
  color: var(--wt-ink-strong);
  background: var(--wt-paper);
  overflow-wrap: anywhere;
}
.cp-msg[data-who="guide"] .cp-bubble { background: var(--wt-hatch); }
.page-content .cp-bubble p { font-family: inherit; font-size: 1em; line-height: 1.68; margin: 0 0 0.65em; }
.page-content .cp-bubble p:last-child { margin: 0; }
.page-content .cp-bubble em, .page-content .cp-bubble i { font-style: italic; }
/* The topic statement in Guide's opening message.
   The transcript pages set it centred and italic; the product does neither.
   Bold and left-aligned, like the rest of the message it is part of — a
   statement two people are about to argue about is the substance of that
   message, not a pull quote from it.
   `p.center` is the transcript's own class and it is a safe hook: there is
   exactly one of them in each of the sixteen chats, always in Guide's first
   message, always the statement. Everywhere else <em> stays italic, because
   everywhere else it is emphasis. */
.page-content .cp-bubble p.center { text-align: left; }
.page-content .cp-bubble p.center em { font-style: normal; font-weight: 600; }
.page-content .cp-bubble strong, .page-content .cp-bubble b { font-weight: 600; }
.page-content .cp-bubble ul { margin: 0.4em 0; padding-left: 1.2em; }
.page-content .cp-bubble li { font-family: inherit; font-size: 1em; line-height: 1.62; margin: 0 0 0.25em; }

/* A message the reader has not been given yet — the closing exchange, which
   arrives when they reach the end. */
.cp-msg.is-ahead, .cp-sep.is-ahead { display: none; }
.cp-msg.is-new { animation: cp-rise 0.36s cubic-bezier(0.22, 1, 0.36, 1) both; }
@keyframes cp-rise {
  from { opacity: 0; transform: translateY(0.6rem); }
  to { opacity: 1; transform: none; }
}

/* ---- how long the two of them left it ----
   A Sway chat is not a half-hour sitting; it runs over evenings, and the
   pauses are part of what it is. */
.cp-sep {
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  margin: 0.4rem 0;
  font-family: "Roboto Mono", monospace;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.cp-sep::before, .cp-sep::after {
  content: "";
  flex: 1;
  height: 1.5px;
  background: var(--wt-faint);
  opacity: 0.55;
}

/* ---- the typing indicator ----
   It shows on the *other* student's screen: while one of them is typing, the
   other is watching three dots. It belongs to the closing exchange and to
   nothing else. */
.cp-typing { display: none; }
.cp-typing.is-on { display: flex; }
.cp[data-me="left"] .cp-typing[data-mine], .cp[data-me="right"] .cp-typing[data-mine] { display: none; }
.cp-typing .cp-bubble { display: flex; align-items: center; gap: 0.3rem; padding: 0.95rem 1rem; }
.cp-typing i {
  width: 0.35rem;
  height: 0.35rem;
  border-radius: 50%;
  background: var(--wt-ink);
  animation: cp-typedot 1.1s ease-in-out infinite;
}
.cp-typing i:nth-child(2) { animation-delay: 0.14s; }
.cp-typing i:nth-child(3) { animation-delay: 0.28s; }
@keyframes cp-typedot {
  0%, 65%, 100% { opacity: 0.28; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-0.2rem); }
}

/* ---- Guide, mid-sentence ----
   The product streams Guide's replies a word at a time, so the closing one
   does here. The caret says the sentence is not finished. */
.cp-caret {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: var(--wt-accent);
  animation: cp-blink 1.1s steps(1, end) infinite;
}
@keyframes cp-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* ---- emoji reactions ----
   Six emoji, no more, and the badge sits over the bottom edge of the bubble
   on the far side from the sender. Drawn in rather than recorded — the
   transcripts do not carry reactions — which is what the caveat says. */
.cp-react {
  position: absolute;
  bottom: -0.7rem;
  right: 0.7rem;
  display: flex;
  align-items: center;
  padding: 0.1rem 0.35rem;
  border: 1.5px solid var(--wt-faint);
  border-radius: 999px;
  background: var(--wt-paper);
  font-size: 0.9rem;
  line-height: 1.3;
}
.cp[data-me="left"] .cp-msg[data-who="left"] .cp-react,
.cp[data-me="right"] .cp-msg[data-who="right"] .cp-react { right: auto; left: 0.7rem; }

/* ---- the message box ----
   The three controls the product has, at the end of the conversation where a
   student would find them. Nobody can type into it — this chat already
   happened — so it sits quiet. */
.cp-composer {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 1.2rem max(0px, calc((100% - 58rem) / 2)) 0;
  opacity: 0.5;
}
.cp-emoji { flex: none; font-size: 1.1rem; line-height: 1; color: var(--text-soft); }
.page-content .cp-say {
  flex: 1;
  min-width: 0;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--text-soft);
  margin: 0;
  padding: 0.45rem 1rem;
  border: 1.5px dashed var(--wt-faint);
  border-radius: 18px;
  background: var(--wt-paper);
  overflow: hidden;
  white-space: nowrap;
}
.cp-send {
  flex: none;
  border: 1.5px solid var(--wt-accent);
  border-radius: 4px;
  background: var(--wt-accent);
  color: var(--cp-on-accent);
  font-family: "Roboto Mono", monospace;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 0.55rem 1.1rem;
}

/* ============================================================
   6. The notice between the chat and the quiz
   ============================================================ */
/* The product ends a chat with a modal across the screen. Here it arrives when
   the reader reaches the end of the conversation and then stays put, as the
   join between the chat and the quiz — a sheet they had to dismiss before they
   could carry on scrolling would be the page fighting the one thing it asks
   them to do. */
.cp-notify {
  display: flex;
  justify-content: center;
  margin: 1.6rem 0 0;
}
.cp-notify[hidden] { display: none; }
.cp-modal {
  width: min(24rem, 100%);
  border: 1.5px solid var(--wt-ink);
  border-radius: 5px;
  background: var(--wt-paper);
  box-shadow: 4px 4px 0 var(--wt-cast);
}
.cp-notify.is-new .cp-modal { animation: cp-rise 0.34s cubic-bezier(0.22, 1, 0.36, 1) both; }
.cp-modal-head {
  padding: 0.55rem 0.8rem;
  border-bottom: 1.5px solid var(--wt-faint);
  background: var(--wt-hatch);
  font-family: "Roboto Mono", monospace;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
.page-content .cp-modal-body p {
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--wt-ink-strong);
  margin: 0;
  padding: 0.9rem 0.8rem;
}
.cp-modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; padding: 0 0.8rem 0.8rem; }
.cp-modal-btn {
  border: 1.5px solid var(--wt-faint);
  border-radius: 4px;
  background: var(--wt-paper);
  padding: 0.45rem 0.8rem;
  font-family: "Roboto Mono", monospace;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
  cursor: pointer;
}
.cp-modal-btn.is-primary { border-color: var(--wt-accent); background: var(--wt-accent); color: var(--cp-on-accent); }
.cp-modal-btn:focus-visible { outline: 2px solid var(--wt-accent); outline-offset: 2px; }

.cp-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  border: 1.5px solid var(--wt-ink-strong);
  border-radius: 4px;
  background: var(--wt-paper);
  padding: 0.55em 1em;
  font-family: "Roboto Mono", monospace;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--wt-ink-strong);
  box-shadow: 2px 2px 0 var(--wt-cast);
  cursor: pointer;
  transition: box-shadow 0.14s ease, transform 0.14s ease;
}
/* Hover deepens the cast; it does not recolour the edge. Pressed goes the
   other way. */
.cp-btn:hover { box-shadow: 3px 3px 0 var(--wt-cast); transform: translate(-1px, -1px); }
.cp-btn:active { box-shadow: 1px 1px 0 var(--wt-cast); transform: translate(1px, 1px); }
.cp-btn:focus-visible { outline: 2px solid var(--wt-accent); outline-offset: 2px; }
.cp-btn.is-go { border-color: var(--wt-accent); background: var(--wt-accent); color: var(--cp-on-accent); }

/* ============================================================
   7. Two screens, side by side
   ============================================================ */
.page-content .cp-actlead {
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--text-soft);
  margin: 0 0 1rem;
}
.cp-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.9rem, 1.8vw, 1.8rem);
  align-items: start;
}
.cp-half { min-width: 0; }
.cp-label { display: flex; align-items: baseline; gap: 0.55rem; margin: 0 0 0.45rem; }
.page-content .cp-label b {
  font-family: "Roboto Mono", monospace;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
/* A dashed, unfilled, uppercase mono word for what a thing *is* — the .wp-tag
   shape, restated because this sheet does not load wire-page.css. */
.cp-side-tag {
  font-family: "Roboto Mono", monospace;
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-soft);
  border: 1.5px dashed var(--wt-faint);
  border-radius: 3px;
  padding: 0.1em 0.45em;
}

/* These two screens keep a frame, because there are two of them and the frame
   is what says they are separate screens. They have no height of their own
   either, and nothing to clip: the page scrolls, not the box. */
.cp-win {
  position: relative;
  border: 2px solid var(--wt-ink);
  border-radius: 6px;
  background: var(--wt-paper);
  box-shadow: 5px 5px 0 var(--wt-cast);
  color: var(--wt-ink);
}
.cp-app {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.45rem 0.75rem;
  border-bottom: 1.5px solid var(--wt-faint);
  background: var(--wt-hatch);
  font-family: "Roboto Mono", monospace;
}
.cp-topnav { margin-left: auto; display: flex; align-items: center; gap: 0.6rem; }
/* The theme control in the product's top bar is Material's brightness_auto —
   a capital A in a rounded square — not a moon. */
.cp-abadge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  border: 1.5px solid var(--wt-faint);
  border-radius: 4px;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  color: var(--wt-ink-strong);
}

.cp-sheet { padding: 1.1rem 1.3rem 1.2rem; display: flex; flex-direction: column; }
.page-content .cp-h {
  font-family: "Roboto Mono", monospace;
  font-size: 0.86rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--wt-ink-strong);
  margin: 0 0 0.35rem;
}
.page-content .cp-p {
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.86rem;
  line-height: 1.62;
  color: var(--wt-ink-strong);
  margin: 0 0 0.6rem;
}
.page-content .cp-p.is-soft { color: var(--text-soft); }
.page-content .cp-p b { font-weight: 600; }

/* ---- the understanding quiz ---- */
/* A column, so that the mark sits on the card's floor. Pairs of cards are
   given a common height (see alignRows in js/chat-player.js), and whichever
   of the two has less in it gets the slack — put below the mark it reads as a
   box that failed to fill, and put above it as a card with room in it. */
.cp-quiz {
  display: flex;
  flex-direction: column;
  border: 1.5px solid var(--wt-ink);
  border-radius: 5px;
  padding: 0.85rem 0.9rem;
  margin-bottom: 0.7rem;
}
.page-content .cp-quiz > b {
  display: block;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.86rem;
  font-weight: 600;
  line-height: 1.55;
  color: var(--wt-ink-strong);
  margin-bottom: 0.6rem;
}
.page-content ul.cp-answers { list-style: none; margin: 0; padding: 0; }
.page-content ul.cp-answers li {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  border-radius: 4px;
  background: var(--wt-hatch);
  padding: 0.5rem 0.6rem;
  margin: 0 0 0.3rem;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--wt-ink);
  transition: background-color 0.24s ease, color 0.24s ease;
}
.page-content ul.cp-answers li.is-on { background: var(--wt-accent-soft); color: var(--wt-ink-strong); }
.cp-radio {
  flex: none;
  display: inline-block;
  width: 0.85rem;
  height: 0.85rem;
  margin-top: 0.3rem;
  border-radius: 50%;
  border: 1.5px solid var(--wt-ink);
  transition: transform 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}
.cp-answers li.is-on .cp-radio, .cp-radio.is-on {
  border-color: var(--wt-accent);
  background: var(--wt-accent);
  box-shadow: inset 0 0 0 2px var(--wt-paper);
}
.cp-radio.is-landing { transform: scale(1.4); }

/* Whether the answer they picked was the right one. Sway marks it, so a
   wireframe that left it out would be a drawing of a screen that does not
   exist — but it is said in words, in the mono a label is set in, because
   this drawing has no success colour and no danger colour. */
/* Hidden, not absent. A card that grows by a line the moment it is marked
   changes height under the alignment that has already been measured — and
   the two columns are only worth putting side by side if question three is
   level with question three. Reserving the line also stops each card jumping
   as the answers land. */
.page-content .cp-verdict {
  visibility: hidden;
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: "Roboto Mono", monospace;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: auto 0 0;
  padding-top: 0.5rem;
}
.cp-quiz.is-marked .cp-verdict { visibility: visible; }
.cp-verdict b {
  border: 1.5px solid var(--wt-ink-strong);
  border-radius: 3px;
  padding: 0.05em 0.45em;
  font-weight: 700;
  color: var(--wt-ink-strong);
}
.cp-verdict.is-wrong b { border-style: dashed; border-color: var(--wt-faint); color: var(--text-soft); }

/* ---- the post-chat survey ---- */
/* The eight pixels are the room the strokes need: the outline overshoots the
   table by seven, and without somewhere to put it the drawn edge either lands
   inside the table — a line down the middle of the first column, a hair from
   the statements — or pushes the wrapper into overflow, which brings up a
   scrollbar, which changes this container's inline size, which resizes the
   column heads (they are set in cqi), which resizes the table, which rebuilds
   the strokes. That last one is a loop, and it hangs the tab. Padding breaks
   it at the source: the strokes live in the padding and never widen anything.
   The negative margin keeps the table where it was. */
.cp-matrixwrap {
  overflow-x: auto;
  container-type: inline-size;
  padding: 8px;
  margin: 0.2rem -8px 0.8rem;
}
.page-content table.cp-matrix {
  width: 100%;
  border-collapse: collapse;
  border: 1.5px solid var(--wt-ink);
  background: var(--wt-paper);
  box-shadow: none;
  border-radius: 0;
  margin: 0;
  font-family: "Roboto Mono", monospace;
  table-layout: fixed;
}
.page-content table.cp-matrix th,
.page-content table.cp-matrix td {
  border: 1.5px solid var(--wt-faint);
  padding: 0.45rem 0.25rem;
  text-align: center;
  line-height: 1.35;
  vertical-align: middle;
  font-weight: 400;
}
/* A head is hatched, never filled: a filled head reads as a selected row. */
.page-content table.cp-matrix th {
  background: var(--wt-hatch);
  color: var(--wt-ink-strong);
  font-weight: 700;
}
/* The statement column carries prose, and prose needs air on both sides of
   it. The radio columns stay tight — they hold a dot. */
.page-content table.cp-matrix td:first-child,
.page-content table.cp-matrix th:first-child {
  text-align: left;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--wt-ink-strong);
  width: 40%;
  padding: 0.5rem 0.7rem 0.5rem 0.75rem;
}
/* Columns that mean equal steps are drawn as equal steps — a scale whose
   columns size themselves to their labels draws a ruler with uneven marks on
   it. The head type is sized against the table rather than the window, so it
   holds at every width instead of only at one. */
.page-content table.cp-matrix th:not(:first-child) {
  padding-left: 0.15rem;
  padding-right: 0.15rem;
  font-size: min(0.62rem, 2.1cqi);
  letter-spacing: -0.01em;
}
.page-content table.cp-matrix.is-bare td:first-child,
.page-content table.cp-matrix.is-bare th:first-child { width: auto; text-align: center; }
.page-content table.cp-matrix.is-bare th { font-size: min(0.62rem, 2.1cqi); }

/* Where the student was before they started. A hollow ring the filled dot
   moves away from, so the row says what changed as well as what it ended at. */
.cp-was {
  display: inline-block;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  border: 1.5px dashed var(--wt-faint);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.cp-was.is-on { opacity: 1; }

.cp-textarea {
  border: 1.5px solid var(--wt-ink);
  border-radius: 4px;
  padding: 0.6rem 0.7rem;
  min-height: 3.2rem;
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--wt-ink-strong);
  background: var(--wt-paper);
}
.cp-textarea.is-empty { color: var(--text-soft); border-style: dashed; border-color: var(--wt-faint); }

.cp-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-top: auto;
  padding-top: 0.9rem;
}
.cp-hint {
  font-family: "Roboto Mono", monospace;
  font-size: 0.66rem;
  line-height: 1.5;
  color: var(--text-soft);
}
.cp-next {
  border: 1.5px solid var(--wt-accent);
  border-radius: 4px;
  background: var(--wt-accent);
  color: var(--cp-on-accent);
  font-family: "Roboto Mono", monospace;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 0.5rem 1.2rem;
}

/* A student who never filled the survey in. Two of the sixteen chats have
   one, and the honest thing is to say so rather than to draw a filled-in page
   that nobody filled in. */
.cp-none {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border: 1.5px dashed var(--wt-faint);
  border-radius: 5px;
  padding: 2.4rem 1.2rem;
}
.page-content .cp-none p {
  font-family: "IBM Plex Sans", sans-serif;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--text-soft);
  max-width: 22rem;
  margin: 0;
}

/* ============================================================
   8. Where the two of them ended up
   ============================================================ */
/* The page's last word, and the only part of it that is not a drawing of a
   screen. Neither side of the axis is coloured and neither direction is the
   good one — colouring one of them would say that one of them is the failure,
   which is the opposite of what the product is for. */
.cp-outcome {
  margin: 2rem 0 0;
  border: 1.5px solid var(--wt-ink);
  border-radius: 5px;
  background: var(--wt-paper);
  box-shadow: 3px 3px 0 var(--wt-cast);
  padding: 1.1rem 1.2rem 1.2rem;
}
.page-content .cp-outcome-title {
  font-family: "Roboto Mono", monospace;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-soft);
  margin: 0 0 1rem;
}
/* One source of truth for the two columns that are not points: the head row,
   every student's row, and the arrow that has to line up with all of them all
   read the same two numbers. */
.cp-axis-head, .cp-arc {
  --cp-name-w: 8rem;
  --cp-shift-w: 4rem;
  --cp-gap: 0.3rem;
  display: grid;
  grid-template-columns: var(--cp-name-w) repeat(7, minmax(0, 1fr)) var(--cp-shift-w);
  gap: var(--cp-gap);
}
.cp-axis-head {
  align-items: end;
  font-family: "Roboto Mono", monospace;
  font-size: 0.6rem;
  line-height: 1.3;
  color: var(--text-soft);
  text-align: center;
  margin-bottom: 0.5rem;
}
.cp-arc { position: relative; align-items: center; margin-bottom: 0.5rem; }
.page-content .cp-arc-name {
  font-family: "Roboto Mono", monospace;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.page-content .cp-arc-name span {
  display: block;
  font-size: 0.58rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.cp-slot { position: relative; height: 1.6rem; z-index: 1; }

/* ---- which way they moved ----
   An arrow from where they started to where they ended: one line across the
   ground they actually covered, with the head at the end they arrived at. It
   is drawn in ink rather than in the accent, because the accent is already
   carrying "after" on the filled dot and a second accent mark on the same row
   would be saying the same thing twice.

   Spans the seven point columns and positions itself inside them, so --a and
   --b are simply the centres of the two columns as percentages — no counting
   grid tracks, and no arithmetic that has to agree with the number of points
   on the scale. */
/* Out of the grid entirely. Placed as a grid item it took a cell of its own,
   and every dot after it shifted along a column — the arrow was right and the
   figure it was annotating was wrong. Absolutely positioned across exactly
   the seven point columns instead, so --a and --b stay simple percentages of
   the scale and nothing else on the row moves. */
.cp-move {
  position: absolute;
  top: 50%;
  left: calc(var(--cp-name-w) + var(--cp-gap));
  right: calc(var(--cp-shift-w) + var(--cp-gap));
  height: 1.5px;
  margin-top: -0.75px;
  pointer-events: none;
}
/* The shaft, stopping short of both dots. */
.cp-move::before {
  content: "";
  position: absolute;
  top: 0;
  left: calc(min(var(--a), var(--b)) + 0.34rem);
  right: calc(100% - max(var(--a), var(--b)) + 0.34rem);
  height: 1.5px;
  background: var(--wt-ink);
}
/* The head, its tip on the dot they arrived at. */
.cp-move::after {
  content: "";
  position: absolute;
  top: 50%;
  left: var(--b);
  width: 0;
  height: 0;
  border-top: 0.3rem solid transparent;
  border-bottom: 0.3rem solid transparent;
  border-left: 0.46rem solid var(--wt-ink);
  transform: translate(-0.8rem, -50%);
}
.cp-move.is-back::after {
  border-left: 0;
  border-right: 0.46rem solid var(--wt-ink);
  transform: translate(0.34rem, -50%);
}
.cp-pt {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.72rem;
  height: 0.72rem;
  margin: -0.36rem 0 0 -0.36rem;
  border-radius: 50%;
}
.cp-pt.is-pre { border: 1.5px solid var(--wt-ink); }
.cp-pt.is-post { background: var(--wt-accent); }
/* One point standing for two readings, for a student who ended where they
   started. Half the ring they arrived with, half the fill they left with —
   before on the left and after on the right, the way the two of them are
   ordered everywhere else on this figure. */
.cp-pt.is-both {
  border: 1.5px solid var(--wt-ink);
  background: linear-gradient(to right, transparent 0 50%, var(--wt-accent) 50% 100%);
}
.page-content .cp-arc-shift {
  font-family: "Roboto Mono", monospace;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--wt-ink-strong);
  text-align: center;
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.page-content .cp-arc-shift.is-still { font-weight: 400; color: var(--text-soft); }
.page-content .cp-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  margin: 0.9rem 0 0;
  padding-top: 0.8rem;
  border-top: 1.5px dashed var(--wt-faint);
  font-family: "Roboto Mono", monospace;
  font-size: 0.62rem;
  color: var(--text-soft);
}
.cp-legend span { display: inline-flex; align-items: center; gap: 0.45em; }
.cp-key { width: 0.72rem; height: 0.72rem; border-radius: 50%; flex: none; }
.cp-key.is-pre { border: 1.5px solid var(--wt-ink); }
.cp-key.is-post { background: var(--wt-accent); }
.cp-key.is-both {
  border: 1.5px solid var(--wt-ink);
  background: linear-gradient(to right, transparent 0 50%, var(--wt-accent) 50% 100%);
}

/* ============================================================
   9. Narrow
   ============================================================ */
@media (max-width: 900px) {
  .cp-pair { grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
  .cp-axis-head, .cp-arc { --cp-name-w: 5rem; --cp-shift-w: 2.8rem; }
  .cp-axis-head { font-size: 0.52rem; }
  .page-content .cp-arc-name { font-size: 0.7rem; }
  .cp-msg { max-width: 92%; }
  .cp-swap-txt { display: none; }
  .cp-topbar-in { gap: 0.6rem; }
}
@media (max-width: 620px) {
  .cp-bubble { font-size: 0.92rem; padding: 0.6rem 0.75rem; }
  .cp-outcome { padding: 0.9rem; }
  .cp-axis-head, .cp-arc { --cp-name-w: 3.8rem; --cp-shift-w: 2.2rem; }
  .cp-strip-prog span:first-child { display: none; }
  .cp-prog-track { width: 3rem; }
}

/* ============================================================
   10. Ink
   ============================================================ */
/* js/hand-ink.js replaces a CSS border with generated strokes; this is the
   half of the arrangement that belongs to the page, restated against this
   sheet's own switch class and scoped to this page's own container — the same
   way next-paths.css and gallery-wire.css each carry their own copy, so that
   a page can be drawn without loading a stylesheet it has no other use for.

   html.cp-ink is set by the script, and is what takes the CSS border off. A
   reader with no JavaScript keeps the ruled box, which is the same design a
   shade quieter.

   The reveal exists but is almost never played: js/chat-player.js freezes
   every box as it is made, so they arrive drawn rather than drawing
   themselves. A box that was on the page when the page was has never come
   into being as far as the reader is concerned. The three things that play it
   are a message in the closing exchange, and the quiz and survey screens. */
html.cp-ink .cp .hi-box {
  position: relative;
  border-color: transparent;
  /* Squared: the generated outline has square corners, and a rounded fill
     under it shows through where the strokes cross. */
  border-radius: 0;
}
/* The rule above sets `position: relative`, so that the absolutely positioned
   stroke layer resolves against the box it was drawn for. That is right for
   every box on this page except the one that has to stay with the reader:
   sticky is a positioned containing block too, and it is what the bar is for.
   Without this the bar silently scrolls away, which is the whole thing it
   exists not to do. */
html.cp-ink .cp .cp-topbar.hi-box { position: sticky; border-radius: 0; }
.cp .hi-ink {
  position: absolute;
  left: -7px;
  top: -7px;
  overflow: visible;
  pointer-events: none;
  fill: none;
  z-index: 3;
}
.cp .hi-ink.hi-inset { left: 0; top: 0; }
.cp .hi-ink path {
  stroke: var(--wt-ink);
  stroke-width: 1.15;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
/* The second pass over an edge is lighter, as a real second pass is. */
.cp .hi-ink path.hi-p2 { stroke-width: 0.85; stroke-opacity: 0.5; }
.cp .hi-faint path { stroke: var(--wt-faint); stroke-width: 1; }
.cp .hi-accent path { stroke: var(--wt-accent); stroke-width: 1.2; }
.cp .hi-dash path { stroke-dasharray: 4 3.5; }

/* pathLength="1" normalises every stroke, so one dash length covers any edge
   and a long line takes no longer to draw than a short one. */
@keyframes cp-draw { to { stroke-dashoffset: 0; } }
@keyframes cp-inkin { to { opacity: 1; } }
html.cp-ink .cp .hi-ink:not(.hi-dash) path { stroke-dasharray: 1; stroke-dashoffset: 1; }
html.cp-ink .cp .hi-on > .hi-ink:not(.hi-dash) path {
  animation: cp-draw 0.34s cubic-bezier(0.4, 0.05, 0.25, 1) forwards;
  animation-delay: calc(min(var(--b, 0), 14) * 0.03s + var(--i, 0) * 0.03s);
}
/* A dashed line cannot be revealed by its dash pattern — that pattern is what
   the dashes are made of — so it comes in edge by edge on the same beat. */
html.cp-ink .cp .hi-dash path { opacity: 0; }
html.cp-ink .cp .hi-on > .hi-dash path {
  animation: cp-inkin 0.28s ease forwards;
  animation-delay: calc(min(var(--b, 0), 14) * 0.03s + var(--i, 0) * 0.03s);
}

/* ============================================================
   11. Reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html.cp-ink .cp .hi-ink:not(.hi-dash) path { stroke-dashoffset: 0; animation: none; }
  html.cp-ink .cp .hi-dash path { opacity: 1; animation: none; }
  .cp-typing i, .cp-caret { animation: none; }
  .cp-typing i { opacity: 0.55; }
  .cp-caret { opacity: 1; }
  .cp-msg.is-new, .cp-notify.is-new .cp-modal { animation: none; }
  .cp-answers li, .cp-radio, .cp-was { transition: none; }
}
