/* =============================================================================
 * kc-lesson-tutor.css — the lesson tutor panel
 * =============================================================================
 *
 * WHAT THIS FILE IS FOR
 *
 * A member stuck on a quiz question opens one surface and asks Invictus to
 * explain the IDEA. This file is the geometry that lets that happen without
 * moving anything the member was about to press.
 *
 * -----------------------------------------------------------------------------
 * THE THREE MEASUREMENTS THIS FILE RESTS ON
 * -----------------------------------------------------------------------------
 *
 * M1. `position: fixed` INSIDE `.kc-v2-card` IS CARD-RELATIVE AND IS CLIPPED.
 *     kc-lesson-v2.css:167-174 gives the card `position: relative; overflow:
 *     hidden`, and kc-lesson-uplift.css:394-400 gives it `backdrop-filter:
 *     var(--blur)`. A backdrop-filter ancestor is a containing block for a
 *     fixed descendant. Measured at 1280x844, a fixed probe inside the card
 *     landed flush to the CARD (top 402 / left 151) rather than the viewport.
 *     Worse, the two surfaces would differ: m.html does not load
 *     kc-lesson-uplift.css, so the same markup escapes to the viewport there.
 *     THE PANEL IS THEREFORE NEVER A DESCENDANT OF `.kc-v2-card`. It is a
 *     direct child of `<kc-lesson-runner>`, appended once by `_ensureTutor()`
 *     and preserved across `_setView`.
 *
 * M2. A FIXED DIRECT CHILD OF THE CUSTOM ELEMENT RESOLVES TO THE VIEWPORT, on
 *     both surfaces. This depends on ONE INVARIANT, and it is written here
 *     because nothing else states it: `main.kc-v2-route` must carry no
 *     `animation: pageIn`. center.html:519-522 documents why — `animation` with
 *     `fill-mode: both` leaves an IDENTITY MATRIX on the element rather than
 *     `transform: none`, and a transformed ancestor is a containing block for a
 *     fixed descendant. The lesson route is the one route that does not carry
 *     it (center.html:2354, m.html:1278). If that ever changes, this panel
 *     docks to the route instead of the window.
 *
 * M3. EVERY BUTTON IS FORCED TO `--target-min` SQUARE, WITH `!important`.
 *     kc-theme.css:1432-1442, and both mounts carry `.kc-a11y-targets`. Touch
 *     targets are therefore free and there is no "make it visually smaller"
 *     escape hatch. NO `min-height` IS DECLARED IN THIS FILE — it would
 *     duplicate a number the register owns (44px, 48px in `clear`).
 *
 * -----------------------------------------------------------------------------
 * WHY A NON-MODAL BOTTOM SHEET
 * -----------------------------------------------------------------------------
 *
 * IT CANNOT MOVE THE FOOTER, because nothing in the card's layout is a function
 * of the panel's state: the panel is out of flow, viewport-anchored, and
 * `_openTutor()` calls `_renderTutor()` and never `render()`. An inline drawer
 * would move the primary control by its own height AND AGAIN WITH EVERY TURN OF
 * THE CONVERSATION — the same defect kc-lesson-beats.css was built to remove
 * (its header records the card growing 340 -> 966px and the primary travelling
 * 685 -> 1309px, "away from the pointer about to press it"). Text you have not
 * received yet has no height to reserve, so that pinning cannot be rebuilt.
 *
 * NOT A MODAL. A modal needs a focus trap, which the brief forbids, and a modal
 * without one is a lie to assistive tech. It would also hide the question the
 * member is stuck on, and `<dialog>.showModal()` puts the panel in the top
 * layer above `.kc-v2-balloons` (z 90), `.kc-v2-xp-fly` (z 110) and the shell's
 * own dialog (z 90), every one of which would have to be re-reasoned. z-index
 * 85 sits under all of them and nothing existing changes.
 *
 * NOT A SIDE SHEET. At 1280x844 the card's right edge is at x=1164 — 116px of
 * clearance — and `.kc-v2-footer` is `justify-content: space-between`, so the
 * secondary control ("Previous part") sits at the card's right edge, directly
 * under where a side sheet would be. A side sheet occludes a live control.
 *
 * A horizontal band never occludes a control that lives at the card's right
 * edge, and `min(100%, 760px)` is one geometry for both surfaces: a full-bleed
 * drawer at 390 and a centred drawer over the reading column at 1280.
 *
 * KNOWN AND ACCEPTED: center.html's toast (`position: fixed; z-index: 95`)
 * lands on top of the sheet. It is transient and it should be on top. On
 * `m.html?full=1` the phone bezel is `overflow: hidden` but is not a containing
 * block for `position: fixed`, so the sheet renders across the browser window
 * rather than inside the bezel — a review-tooling artefact only.
 *
 * -----------------------------------------------------------------------------
 * TOKENS, AND BOTH SURFACES
 * -----------------------------------------------------------------------------
 *
 * kc-boot.js redirects every viewport <= 767px to /m, and m.html loads neither
 * kc-uplift.css nor kc-lesson-uplift.css: `--plate / --inset / --ink1 / --ink2 /
 * --ink3 / --acc / --quiet / --rule / --track / --crit / --shadow / --blur` do
 * not exist there at all. EVERY colour below is therefore written
 * `var(--uplift-token, var(--theme-token))`. No raw hex, no raw px font size,
 * no raw millisecond. `--font-display` is the presence slot only
 * (kc-theme.css:376-380) and appears nowhere here: everything in this panel is
 * either typed into or read as prose.
 *
 * NO GLASS WHERE IT IS FORBIDDEN, WITHOUT A BRANCH. `--blur` and `--shadow` are
 * already `none` in `clear` (kc-uplift.css:709-710) and `solid`
 * (kc-uplift.css:1029), and neither token exists on m.html, so
 * `backdrop-filter: var(--blur, none)` self-disables in four of the twelve
 * presentation x surface combinations without a rule of its own. It appears
 * ONCE, on the sheet's outermost surface. The thread turns are `--inset` over
 * `--plate` and carry NO backdrop-filter (kc-uplift.css:283-284, kc-ask.css:
 * 872-873).
 *
 * AN EDGE, UNCONDITIONALLY. `--shadow` is `none` in `clear`, and a sheet that
 * separates from the page only by shadow has no edge there. `border: var(--bw-1)
 * solid var(--rule, var(--border))` is declared once, in every register:
 * near-invisible where the shadow does the work, and the sole separator where
 * it does not. One rule, no branch.
 *
 * -----------------------------------------------------------------------------
 * MOTION, AND THE STATIC EQUIVALENT
 * -----------------------------------------------------------------------------
 *
 * Two gestures exist and both depart from the base state and RETURN to it, so
 * all three degradations land on the finished appearance:
 *
 *   `clear`        -> kc-theme.css:2001-2010 sets `animation: none !important`
 *   reduced motion -> kc-theme.css:2089-2096 sets the same
 *   0ms durations  -> --motion-* are `0ms !important` in both
 *
 *   G1 RISE     the sheet arrives from below   -> static: it is simply THERE,
 *               at rest, in place. `--kc-t-rise` is `0em` in `clear`, so even a
 *               future rule that forgot a guard has nothing left to travel.
 *   G2 SHIMMER  work is in progress            -> static: a flat `--quiet` bar
 *               with a marked leading edge. A DIFFERENT SIGN, not a slower
 *               animation, and the words ("Working on that.") carry it either
 *               way. This file declares its own `prefers-reduced-motion` block
 *               rather than relying on the shell's, because there is no
 *               guarantee `[data-presentation]` is on the document of every
 *               page that mounts this runner.
 *
 * COLOUR IS NEVER ALONE. The two turn kinds differ by their label word ("You
 * asked" / "Invictus") and by the PRESENCE of a leading edge, not by its hue.
 * The failed and rate-limited blocks differ by a word and a drawn mark, not by
 * fill. Switch the hue channel off and every state is still readable.
 *
 * NO BUBBLES, EVER. kc-ask.css §12 carried forward and scoped to
 * `.kc-v2-tutor`: no asymmetric radius, no alternating alignment, no avatar, no
 * typing dots. The attribute is `data-turn`, deliberately not `data-role` —
 * §12's own selectors are scoped to `.kc-ask-answered` and do not reach here,
 * so the RULE is what is reused, not the selector.
 *
 * -----------------------------------------------------------------------------
 * CONTRAST — measured in the browser, not asserted
 * -----------------------------------------------------------------------------
 * tools/contrast_audit.mjs reads the aurora token files and its manifest is
 * pinned at 65 pairs by tests/contrast.test.js, so these were measured instead
 * from the COMPOSITED grounds of the running panel: the page, then the sheet's
 * `--plate`, then whatever each block paints over it. 1280x900, panel open and
 * answered. tests/lessontutorpanel.test.js re-measures the `clear` column on
 * every run, so these numbers cannot quietly rot.
 *
 * `focused`'s floor for body ink is 4.5:1; `clear`'s is 7:1.
 *
 *   ink                     over                    foc/light  foc/dark   clear
 *   --ink1 answer prose     --inset over --plate      16.74     13.14     15.72
 *   --ink1 member question  --quiet over --plate      15.89     12.46     15.72
 *   --ink2 turn label       --inset over --plate       6.62      6.84     12.59
 *   --ink2 contract line    --plate                    7.34      7.80     14.36
 *   --ink2 foot line        --plate                    7.34      7.80     14.36
 *   --ink1 opener label     --quiet over the card     15.89     12.46     15.72
 *   --ink2 opener note      --quiet over the card      6.62      6.84     12.59
 *   --on-acc send ink       --acc                      5.26      5.59      9.30
 *
 * THE `clear` COLUMN IS ONE NUMBER BECAUSE THE REGISTER MAKES IT ONE.
 * kc-uplift.css:660-665: in `clear` a glass root gets opaque plates, no blur,
 * no wash and no shadow, "which is why `clear` measures identically in all four
 * theme x surface modes". The one combination that moves it is `clear` x
 * `data-scheme="light"` x `data-accent="purple"`, the only light `clear` ramp
 * kc-theme.css:953 declares, and it measures HIGHER throughout (answer prose
 * 16.06, turn label 12.75, contract 14.50, send ink 9.38).
 *
 * THE TURN LABEL SITS ON `--inset`, NOT ON `--acc-quiet`. `--ink2` over
 * `--acc-quiet` over a plate measures 4.65, below the 7:1 floor, and that is
 * the reason the label's surface is the inset one.
 *
 * -----------------------------------------------------------------------------
 * SCOPE AND LOAD ORDER
 * -----------------------------------------------------------------------------
 *   SCOPE. Every selector below is `.kc-v2-tutor`, `.kc-v2-help` or a
 *   descendant of one of them. Nothing is scoped to `.kc-v2-route`,
 *   deliberately: the phone mounts the same runner.
 *
 *   ORDER. AFTER kc-lesson-beats.css in both files — center.html after line
 *   177, m.html after line 71. The runner also appends it itself
 *   (`ensureBeatStyles()` in kc-lesson-v2.js), and that is not belt and braces:
 *   the panel's CLOSED state is `display: none` declared here, so a page that
 *   mounted the runner without this file would render the panel as an ordinary
 *   in-flow block at the foot of the runner — measured at +55px on the primary
 *   control, which is the one defect kc-lesson-beats.css exists to prevent.
 * ========================================================================== */


/* =============================================================================
 * 1. CONSTANTS — three, none of them a colour
 * -----------------------------------------------------------------------------
 * Declared on the bare `[data-presentation]` as well as `:root` so a preview
 * fragment carrying the attribute recomputes against its OWN register rather
 * than inheriting the page's — the same reason kc-theme.css lists both
 * selectors in its reduce block, and the same reason kc-lesson-beats.css does.
 * ========================================================================== */
:root,
[data-presentation] {
  /* How far the sheet travels on arrival. In `em` so it tracks the member's OS
     text size instead of undoing it. */
  --kc-t-rise:  .6em;
  /* The arrival, and the shimmer's cycle. Derived, never literal: --motion-slow
     is 0ms in `clear` and under reduce, so both collapse by ARITHMETIC rather
     than by an override someone could forget. */
  --kc-t-in:    calc(var(--motion-slow) * 1.4);
  --kc-t-skel:  calc(var(--motion-slow) * 7);
}
:root[data-presentation="vivid"],
[data-presentation="vivid"] { --kc-t-rise: .9em; }

/* `clear` promises that nothing moves unless the reader moved it. The amplitude
   goes to zero here as well as the duration. */
:root[data-presentation="clear"],
[data-presentation="clear"] { --kc-t-rise: 0em; }


/* =============================================================================
 * 2. THE AFFORDANCE — one control, one label, one slot
 * -----------------------------------------------------------------------------
 * The last block of `.kc-v2-step`, immediately before `.kc-v2-footer`. It is a
 * CONSTANT height on every screen of a step, which is what preserves
 * kc-lesson-beats.css's property that the primary control does not move between
 * a two-line beat and a nine-line one.
 *
 * Two registers of emphasis, one behaviour:
 *   data-quiet="false"  gradable steps — a bounded row, a fill, and the honesty
 *                       line, because this is where a member gets stuck.
 *   data-quiet="true"   teaching steps — the same control with no fill, no
 *                       border and no note. One line of secondary ink.
 * ========================================================================== */
.kc-v2-help {
  margin-block-start: 16px;
}

.kc-v2-help-open {
  display: flex;
  align-items: center;
  gap: 12px;
  inline-size: 100%;
  padding: 10px 14px;
  text-align: start;
  background-color: var(--quiet, var(--surface-2));
  border: var(--bw-1) solid var(--rule, var(--border));
  border-radius: var(--r-12);
  color: var(--ink1, var(--text));
  font-family: var(--font-sans);
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--ease),
              border-color var(--motion-fast) var(--ease);
}

.kc-v2-help-mark {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--acc, var(--blue-600));
}
.kc-v2-help-mark .kc-v2-mark { width: 20px; height: 20px; }

.kc-v2-help-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-inline-size: 0;
}

.kc-v2-help-label {
  font-size: var(--fs-14);
  font-weight: 500;
  line-height: 1.35;
}

/* THE HONESTY LINE IS PART OF THE BUTTON'S ACCESSIBLE NAME, deliberately: a
   member who presses this knowing it will not name the answer does not press it
   expecting an answer key, and a screen-reader user gets the contract BEFORE
   the press rather than after. */
.kc-v2-help-note {
  color: var(--ink2, var(--text-2));
  font-size: var(--fs-12);
  line-height: 1.4;
  text-wrap: pretty;
}

.kc-v2-help[data-quiet="true"] .kc-v2-help-open {
  padding-inline: 0;
  background-color: transparent;
  border-color: transparent;
  color: var(--ink2, var(--text-2));
}
.kc-v2-help[data-quiet="true"] .kc-v2-help-label { font-weight: 400; }

@media (hover: hover) {
  .kc-v2-help-open:hover {
    background-color: var(--acc-quiet, var(--blue-050));
    border-color: var(--acc, var(--blue-600));
  }
  .kc-v2-help[data-quiet="true"] .kc-v2-help-open:hover {
    background-color: var(--quiet, var(--surface-2));
    border-color: transparent;
  }
}


/* =============================================================================
 * 3. THE SHEET — viewport-anchored, non-modal, and out of flow
 * -----------------------------------------------------------------------------
 * `hidden` is the closed state: the ATTRIBUTE, so the panel is out of the
 * accessibility tree and out of the tab order, not merely invisible. A landmark
 * that is present-but-empty is worse than one that is absent. The declaration
 * below is redundant with the UA sheet and is here anyway, because
 * `.kc-v2-tutor { display: flex }` two rules down would otherwise beat it.
 *
 * The WRAPPER spans the viewport and is `pointer-events: none`, so the lesson
 * behind it stays clickable everywhere the sheet is not. There is no scrim, no
 * `inert`, no `aria-modal`. The lesson stays fully live: a member can select an
 * option, press "Next part" or grade while reading the explanation. That is
 * what makes this assistance rather than an interruption.
 * ========================================================================== */
.kc-v2-tutor[hidden] { display: none; }

.kc-v2-tutor {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 85;
  display: flex;
  justify-content: center;
  padding: 0 12px 12px;
  pointer-events: none;
}

.kc-v2-tutor-sheet {
  pointer-events: auto;
  /* Declared rather than inherited: kc-theme.css carries no global reset, so
     without this `max-block-size` bounds the CONTENT box and the sheet renders
     28px taller than the number says. Measured at 1280x900: 496px against a
     stated ceiling of 468. */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  inline-size: min(100%, 760px);
  /* `auto` block size with a ceiling: a two-line panel is two lines tall. At
     390x844 a 52dvh sheet still leaves 405px of lesson visible above it, and
     the page is NOT scroll-locked, so a member who wants the question higher
     can scroll it there. */
  block-size: auto;
  max-block-size: min(52dvh, 480px);
  padding: 14px 16px 12px;
  gap: 10px;
  background-color: var(--plate, var(--surface));
  -webkit-backdrop-filter: var(--blur, none);
  backdrop-filter: var(--blur, none);
  border: var(--bw-1) solid var(--rule, var(--border));
  border-radius: var(--r-16);
  box-shadow: var(--shadow, var(--shadow-lg));
  color: var(--ink1, var(--text));
  font-family: var(--font-sans);
  animation: kcTutorRise var(--kc-t-in) var(--ease) both;
}

/* `to` is the base state, so `animation: none`, a 0ms duration and a full run
   all land on the same finished appearance. There is no faster version. */
@keyframes kcTutorRise {
  from { transform: translateY(var(--kc-t-rise)); opacity: 0; }
  to   { transform: none; }
}

.kc-v2-tutor-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* An `<h2>`. The lesson's `.kc-v2-title` stays the only `<h1>` in the view. */
.kc-v2-tutor-title {
  margin: 0;
  color: var(--ink1, var(--text));
  font-family: var(--font-sans);
  font-size: var(--fs-16);
  font-weight: 600;
  line-height: 1.3;
}

.kc-v2-tutor-close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: transparent;
  border: var(--bw-1) solid transparent;
  border-radius: var(--r-999);
  color: var(--ink2, var(--text-2));
  cursor: pointer;
}
.kc-v2-tutor-close .kc-v2-mark { width: 18px; height: 18px; }
@media (hover: hover) {
  .kc-v2-tutor-close:hover {
    background-color: var(--quiet, var(--surface-2));
    border-color: var(--rule, var(--border));
    color: var(--ink1, var(--text));
  }
}

/* THE CONTRACT IS NOT A DISCLAIMER. It is the product's promise about this
   surface, stated to the person it constrains, and it is first. */
.kc-v2-tutor-contract {
  margin: 0;
  color: var(--ink2, var(--text-2));
  font-size: var(--fs-12-5);
  line-height: 1.5;
  text-wrap: pretty;
}


/* =============================================================================
 * 4. THE THREAD — two turns, one shape
 * -----------------------------------------------------------------------------
 * `position: relative` makes this the offsetParent of its children, which is
 * what lets the runner scroll to a new turn by arithmetic instead of
 * `scrollIntoView` — the latter walks the ancestor chain and would move the
 * document behind a fixed panel.
 * ========================================================================== */
.kc-v2-tutor-body {
  position: relative;
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.kc-v2-tutor-empty {
  margin: 0;
  color: var(--ink1, var(--text));
  font-size: var(--fs-14);
  line-height: 1.55;
  text-wrap: pretty;
}

.kc-v2-tutor-starts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.kc-v2-tutor-start {
  padding: 8px 12px;
  background-color: var(--quiet, var(--surface-2));
  border: var(--bw-1) solid var(--rule, var(--border));
  border-radius: var(--r-999);
  color: var(--ink1, var(--text));
  font-family: var(--font-sans);
  font-size: var(--fs-13);
  text-align: start;
  cursor: pointer;
}
@media (hover: hover) {
  .kc-v2-tutor-start:hover { border-color: var(--acc, var(--blue-600)); }
}

.kc-v2-tutor-thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* BOTH TURNS ARE LEFT-ALIGNED BLOCKS IN ONE RADIUS. Two layers of ground —
   an `--inset` value over a `--plate` — because that is the stack the contrast
   numbers in the header were measured against. NO backdrop-filter: an inset
   surface is alpha alone (kc-uplift.css:283-284). */
.kc-v2-tutor-turn {
  padding: 10px 12px;
  border-radius: var(--r-12);
  background-color: var(--plate, var(--surface));
  background-image: linear-gradient(var(--inset, var(--surface-2)), var(--inset, var(--surface-2)));
}
.kc-v2-tutor-turn:focus { outline: none; }

/* THE TWO KINDS DIFFER BY THE PRESENCE OF AN EDGE, NOT BY ITS HUE, and by the
   label word above them. Switch the hue channel off and they are still two
   distinguishable things. */
.kc-v2-tutor-turn[data-turn="you"] {
  background-image: linear-gradient(var(--quiet, var(--surface-2)), var(--quiet, var(--surface-2)));
}
.kc-v2-tutor-turn[data-turn="invictus"] {
  box-shadow: inset var(--bw-2) 0 0 var(--acc, var(--blue-600));
}

/* There is no mono token in kc-theme.css and kc-ask.css's `--au-font-mono` does
   not exist on the phone, so the label is separated by CASE AND TRACKING rather
   than by a family that would resolve to nothing on one of the two surfaces. */
.kc-v2-tutor-who {
  margin: 0 0 4px;
  color: var(--ink2, var(--text-2));
  font-size: var(--fs-11);
  font-weight: 600;
  letter-spacing: .08em;
  line-height: 1.3;
  text-transform: uppercase;
}

.kc-v2-tutor-text {
  margin: 0;
  color: var(--ink1, var(--text));
  font-size: var(--fs-14-5);
  line-height: 1.6;
  text-wrap: pretty;
  white-space: pre-wrap;
}

/* The one question it asks back, marked as a question rather than as more
   prose: the member should be able to see where the answer stopped. */
.kc-v2-tutor-askback {
  margin: 8px 0 0;
  padding-inline-start: 10px;
  border-inline-start: var(--bw-2) solid var(--track, var(--border));
  color: var(--ink1, var(--text));
  font-size: var(--fs-13-5);
  font-style: italic;
  line-height: 1.55;
  text-wrap: pretty;
}


/* =============================================================================
 * 5. THINKING — a skeleton, never a spinner, never typing dots
 * -----------------------------------------------------------------------------
 * kc-ask.css:696-711 is explicit that a captioned spinner is the single
 * strongest chatbot signal left on that screen and that it says nothing. The
 * member's question is already rendered above this, so the panel does not go
 * blank while it waits.
 * ========================================================================== */
.kc-v2-tutor-skel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border-radius: var(--r-12);
  background-color: var(--plate, var(--surface));
  background-image: linear-gradient(var(--inset, var(--surface-2)), var(--inset, var(--surface-2)));
}

/* THE STATIC FORM IS A DIFFERENT SIGN, NOT A SLOWER ANIMATION: a flat quiet bar
   with a marked leading edge, present for exactly as long as the work runs. */
.kc-v2-tutor-skel-bar {
  block-size: 14px;
  border-radius: var(--r-999);
  background-color: var(--quiet, var(--surface-2));
  box-shadow: inset var(--bw-2) 0 0 var(--rule, var(--border));
}
.kc-v2-tutor-skel-bar:nth-child(2) { inline-size: 84%; }
.kc-v2-tutor-skel-bar:nth-child(3) { inline-size: 62%; }

.kc-v2-tutor-skel-note {
  margin: 0;
  color: var(--ink2, var(--text-2));
  font-size: var(--fs-12);
  line-height: 1.5;
}

@media (prefers-reduced-motion: no-preference) {
  :root:not([data-presentation="clear"]) .kc-v2-tutor-skel-bar {
    background-image: linear-gradient(90deg,
      var(--quiet, var(--surface-2)) 0 40%,
      var(--acc-quiet, var(--blue-050)) 50%,
      var(--quiet, var(--surface-2)) 60% 100%);
    background-size: 300% 100%;
    animation: kcTutorSkel var(--kc-t-skel) linear infinite;
  }
}
@keyframes kcTutorSkel {
  from { background-position: 100% 0; }
  to   { background-position: -100% 0; }
}


/* =============================================================================
 * 6. FAILED, AND RATE-LIMITED — which is not a failure
 * ========================================================================== */
.kc-v2-tutor-fail,
.kc-v2-tutor-wait {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 12px;
  border-radius: var(--r-12);
}

/* The one container allowed to be critical, and it still carries a word and a
   drawn mark rather than a colour. */
.kc-v2-tutor-fail {
  background-color: var(--plate, var(--surface));
  background-image: linear-gradient(var(--crit-tint, var(--rose-100)), var(--crit-tint, var(--rose-100)));
}
.kc-v2-tutor-fail-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--crit, var(--rose-500));
  font-size: var(--fs-13);
  font-weight: 600;
  line-height: 1.3;
}
.kc-v2-tutor-fail-head .kc-v2-mark { width: 18px; height: 18px; flex: 0 0 auto; }
.kc-v2-tutor-fail-body {
  margin: 0;
  color: var(--ink1, var(--text));
  font-size: var(--fs-13-5);
  line-height: 1.55;
  text-wrap: pretty;
}
.kc-v2-tutor-retry {
  padding: 8px 14px;
  background-color: var(--plate, var(--surface));
  border: var(--bw-1) solid var(--rule, var(--border));
  border-radius: var(--r-999);
  color: var(--ink1, var(--text));
  font-family: var(--font-sans);
  font-size: var(--fs-13);
  font-weight: 500;
  cursor: pointer;
}

/* BEING ASKED TO WAIT IS NOT AN ERROR AND MUST NOT WEAR THE CRITICAL COLOUR.
   No live countdown: a ticking number is motion and a nag, and in `clear` it
   would have to be removed anyway. */
.kc-v2-tutor-wait {
  background-color: var(--quiet, var(--surface-2));
}
.kc-v2-tutor-wait-head {
  margin: 0;
  color: var(--ink1, var(--text));
  font-size: var(--fs-13);
  font-weight: 600;
  line-height: 1.3;
}
.kc-v2-tutor-wait-body {
  margin: 0;
  color: var(--ink1, var(--text));
  font-size: var(--fs-13-5);
  line-height: 1.55;
  text-wrap: pretty;
}


/* =============================================================================
 * 7. COMPOSE, AND THE BOUNDARY LINE
 * -----------------------------------------------------------------------------
 * No `<form>`: a form would need a submit listener bound and unbound in
 * connectedCallback/disconnectedCallback, and the runner's delegated click
 * handler already reaches the send button. Enter sends, Shift+Enter inserts a
 * newline — the Ask screen's own contract, handled in `_onKeyDown`.
 * ========================================================================== */
.kc-v2-tutor-compose {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

/* `--font-sans`, never `--font-display`: kc-theme.css:376-380 reserves the
   display family for presence slots, and this is a field a member types into. */
.kc-v2-tutor-input {
  box-sizing: border-box;
  flex: 1 1 auto;
  min-inline-size: 0;
  max-block-size: 7em;
  padding: 10px 12px;
  background-color: var(--plate, var(--surface));
  background-image: linear-gradient(var(--inset, var(--surface-2)), var(--inset, var(--surface-2)));
  border: var(--bw-1) solid var(--rule, var(--border));
  border-radius: var(--r-12);
  color: var(--ink1, var(--text));
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  line-height: 1.5;
  resize: vertical;
}
.kc-v2-tutor-input::placeholder { color: var(--ink3, var(--text-3)); }

.kc-v2-tutor-send {
  flex: 0 0 auto;
  padding: 10px 18px;
  background-color: var(--acc, var(--blue-600));
  border: var(--bw-1) solid var(--acc, var(--blue-600));
  border-radius: var(--r-12);
  color: var(--on-acc, var(--acc-ink));
  font-family: var(--font-sans);
  font-size: var(--fs-14);
  font-weight: 600;
  cursor: pointer;
}
.kc-v2-tutor-send[disabled] {
  background-color: var(--quiet, var(--surface-2));
  border-color: var(--rule, var(--border));
  color: var(--ink3, var(--text-3));
  cursor: default;
}

/* The same non-diagnostic boundary the personas already carry, and it points at
   the recap's source list rather than asking the member to take the model's
   word for anything. */
.kc-v2-tutor-foot {
  margin: 0;
  color: var(--ink2, var(--text-2));
  font-size: var(--fs-11-5);
  line-height: 1.45;
  text-wrap: pretty;
}


/* =============================================================================
 * 8. WHAT THIS SURFACE IS NOT — enforced, not merely intended
 * -----------------------------------------------------------------------------
 * kc-ask.css §12's judgement, carried here and scoped to this panel. Every chat
 * affordance says "this is a chatbot", and they arrive one at a time, in good
 * faith, from somebody who has seen another product do it.
 * ========================================================================== */
.kc-v2-tutor [data-turn] {
  border-radius: var(--r-12) !important;   /* never an asymmetric bubble radius */
  margin-inline: 0 !important;             /* never alternating alignment */
}
.kc-v2-tutor [data-typing="true"],
.kc-v2-tutor .typing-dots,
.kc-v2-tutor [data-role="avatar"] { display: none !important; }


/* =============================================================================
 * 9. THE STATIC REGISTER, DECLARED HERE AS WELL
 * -----------------------------------------------------------------------------
 * kc-theme.css already zeroes every duration and kills every animation in
 * `clear` and under reduce, but there is no guarantee `[data-presentation]` is
 * on the document of every page that mounts this runner — so this file states
 * it for its own subtree, exactly as kc-lesson-beats.css:734-751 does.
 * ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .kc-v2-tutor-sheet,
  .kc-v2-tutor-skel-bar,
  .kc-v2-help-open {
    animation: none !important;
    transition: none !important;
  }
  /* The shimmer's replacement is the base state, which is already declared
     above: a flat quiet bar with a marked leading edge. */
  .kc-v2-tutor-skel-bar { background-image: none !important; }
}

:root[data-presentation="clear"] .kc-v2-tutor-sheet,
:root[data-presentation="clear"] .kc-v2-tutor-skel-bar,
:root[data-presentation="clear"] .kc-v2-help-open {
  animation: none !important;
  transition: none !important;
}
:root[data-presentation="clear"] .kc-v2-tutor-skel-bar { background-image: none !important; }


/* =============================================================================
 * 10. FORCED COLOURS
 * -----------------------------------------------------------------------------
 * The system replaces every colour chosen above, so the channels that survive
 * are the ones drawn with a border and with system keywords.
 * ========================================================================== */
@media (forced-colors: active) {
  .kc-v2-tutor-sheet,
  .kc-v2-tutor-turn,
  .kc-v2-tutor-fail,
  .kc-v2-tutor-wait,
  .kc-v2-tutor-skel,
  .kc-v2-help-open { border: 1px solid CanvasText; }
  .kc-v2-tutor-turn[data-turn="invictus"] { border-width: 2px; }
}


/* =============================================================================
 * 11. THE PHONE
 * -----------------------------------------------------------------------------
 * One geometry, two widths. `min(100%, 760px)` is already a full-bleed drawer
 * at 390; what changes below is only the padding that would otherwise eat the
 * measure, and the compose row, which wraps rather than shrinking the field to
 * a slot too small to read what you typed.
 * ========================================================================== */
@media (max-width: 520px) {
  .kc-v2-tutor { padding: 0 8px 8px; }
  .kc-v2-tutor-sheet { padding: 12px 12px 10px; }
  /* The chips wrap to one per row rather than shrinking to a slot too narrow
     to read, and a column of three is still shorter than the answer they save
     the member typing. */
  .kc-v2-tutor-starts { flex-direction: column; align-items: stretch; }
  .kc-v2-tutor-start { text-align: start; }
}
