/**
 * Ask — the sticky assistant card.
 *
 * STICKY, IN FLOW
 *   `.ask` is the last child of the reading column with `position: sticky` and
 *   `bottom`. That combination does something a fixed overlay cannot: while the
 *   rest of the article is still below the fold the card floats at the bottom of
 *   the viewport, and once the reader reaches the end it settles into its real
 *   place in the flow. It therefore never covers the last paragraph, never needs
 *   a dismiss button to get out of the way, and reserves its own space instead
 *   of sitting on top of content.
 *
 *   The card also stays inside the reading column's width, so on a wide screen
 *   it lines up with the text it is talking about rather than pinning itself to
 *   a corner of the window.
 *
 * COLLAPSED IS THE DEFAULT
 *   One line and a chevron. The offer has to be legible at a glance to someone
 *   who is scrolling past it, and everything else — transcript, box, meter —
 *   only appears once they have said yes.
 *
 * HOW IT OPENS
 *   One property, on one element: the card's second grid row goes `0fr → 1fr`.
 *   `fr` interpolates, so this is a real transition rather than a swap, and
 *   because the row is the only thing changing there is nothing for a second
 *   animation to fight with.
 *
 *   It replaces a `hidden` toggle, i.e. `display: none`, which does not
 *   interpolate at all — the panel used to appear at full height in a single
 *   frame while the page reflowed around it, which is what read as the sheet
 *   sticking. The JS side (ui/askCard.ts) does the other half: it draws the
 *   transcript and sizes the writing box BEFORE flipping the class, so the
 *   browser animates a layout that has already settled.
 *
 *   `will-change` is deliberately absent. Promoting a full-width card to its own
 *   layer for a 0.24s transition costs more in memory and paint than the
 *   transition costs in the first place, and this one is already cheap: the
 *   contents are `contain`ed, so the reflow stops at the sheet's edge.
 */

.ask {
  position: sticky;
  bottom: var(--sp-3);
  z-index: 30;
  padding-top: var(--sp-6);
  /* The gap above the card is see-through; only the card itself takes clicks. */
  pointer-events: none;
}

.ask__card {
  display: grid;
  /* The bar keeps its height; the sheet is the row that opens, closes, and
     gives way first when the window is short.
     Both ends are written as `minmax()` on purpose. A track list only
     interpolates when the two sides have the same shape, so `0fr → minmax(0, 1fr)`
     is a swap at the halfway point, not a transition — and it looks exactly like
     the `display` toggle this replaced. `minmax(0, 0fr) → minmax(0, 1fr)` is one
     number changing, which is the thing that animates. */
  grid-template-rows: auto minmax(0, 0fr);
  /* Never taller than the window: the header has to stay reachable, and a card
     whose top is scrolled off screen cannot be closed. */
  max-height: calc(100dvh - var(--sp-16));
  overflow: hidden;
  border: 1px solid var(--blue-line);
  border-radius: var(--r-xl);
  background: linear-gradient(180deg, var(--surface-2), var(--bg-raised));
  box-shadow: var(--shadow-pop);
  pointer-events: auto;
  transition: grid-template-rows var(--dur) var(--ease);
}

.ask.is-open .ask__card { grid-template-rows: auto minmax(0, 1fr); }

/*
 * The row that opens.
 *
 * `min-height: 0` lets a grid row actually shrink below its content — without
 * it the row's minimum is the panel's height and `0fr` resolves to full height,
 * which is the one way this technique silently does nothing.
 *
 * `visibility` is the second half, and its transition is what carries the delay:
 * hidden takes effect only after the collapse has finished, and is dropped
 * immediately on the way open. That keeps the panel paintable for the whole
 * closing animation while still leaving it genuinely invisible at rest — to a
 * reader, to find-in-page, and to the E2E test that asserts the panel is hidden
 * after the bar is clicked a second time.
 */
.ask__sheet {
  min-height: 0;
  overflow: hidden;
  visibility: hidden;
  /* Layout and paint stop at this box, so opening the sheet cannot reflow the
     article above it. */
  contain: layout paint;
  transition: visibility 0s linear var(--dur);
}

.ask.is-open .ask__sheet {
  visibility: visible;
  transition-delay: 0s;
}

/* Before the first open the sheet is not in the layout at all — that is the
   state the prerenderer captures, and the state a crawler is served. */
.ask__sheet[hidden] { display: none; }

/* The open state of the panel's own fade; the resting state is declared with
   the rest of the panel's layout below, so the selector appears exactly once. */
.ask.is-open .ask__panel {
  opacity: 1;
  translate: 0 0;
  transition-delay: calc(var(--dur) / 3);
}

/* ----------------------------------------------------------- the collapsed bar */

/*
 * PROPORTION, PHONE FIRST
 *
 * This card sits on top of the article it is offering to explain, so on a phone
 * every pixel it takes is a pixel of reading it covers. It is therefore sized
 * from the small screen up: the bar is a comfortable 44px tap target and not one
 * pixel more, and the type is one step down from desktop. From `40rem` up, where
 * covering the page is no longer the constraint, both grow a step.
 *
 * `--tap` (44px) is the floor for anything pressable and the ceiling for the
 * collapsed bar — a control that is exactly a thumb is right, and a control that
 * is visibly bigger than a thumb reads as a banner.
 */
.ask__toggle {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  background: none;
  color: var(--text);
  text-align: left;
}

.ask__toggle:hover { background: rgba(47, 111, 208, 0.08); }

/* One line, always: it is the whole offer, and half of it is not an offer. */
.ask__caption {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-sm);
  /* Semi, not bold: it is a label on a control, not a heading. At 13px on a
     phone, bold turns a quiet offer into something that shouts over the article. */
  font-weight: var(--fw-semi);
  letter-spacing: var(--track-tight);
}

/* The mark: the assistant's own badge, so the card is recognisable at a glance. */
.ask__mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 24px;
  height: 24px;
  border: 1px solid var(--blue-line);
  border-radius: 8px;
  background: var(--blue-dim);
  color: var(--blue-bright);
}

.ask__mark--lg { width: 28px; height: 28px; border-radius: 9px; }

@media (min-width: 40rem) {
  .ask__toggle { gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); }
  .ask__caption { font-size: var(--fs-md); font-weight: var(--fw-bold); }
  .ask__mark { width: 28px; height: 28px; border-radius: 9px; }
  .ask__mark--lg { width: 34px; height: 34px; border-radius: 11px; }
}

.ask__cue { color: var(--text-2); transition: rotate var(--dur) var(--ease); }
.ask.is-open .ask__cue { rotate: 180deg; }

/* ------------------------------------------------------------- the panel */

/*
 * The panel: transcript, writing area, meter. Three rows, and the transcript is
 * the only one that flexes — the box and the meter keep their size so the thing
 * a reader reaches for never moves.
 *
 * It also fades and settles as the sheet's row grows. Both are composited
 * properties, so this rides along the height transition without adding a layout
 * pass, and the delay on the way in (see `.ask.is-open .ask__panel` above) keeps
 * the text a beat behind the row — which is what stops it looking squeezed out
 * of a slot that has not finished opening.
 */
.ask__panel {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto auto;
  gap: var(--sp-2);
  min-height: 0;
  padding: var(--sp-2);
  border-top: 1px solid var(--line-soft);
  opacity: 0;
  translate: 0 -0.375rem;
  transition:
    opacity var(--dur-fast) var(--ease),
    translate var(--dur) var(--ease);
}

@media (min-width: 40rem) {
  .ask__panel { gap: var(--sp-3); padding: var(--sp-3); }
}

/* Before the first question there is no transcript, so it takes no space. */
.ask__log[hidden] { display: none; }

/*
 * The transcript. Its height is capped in `dvh` so the card cannot grow past the
 * viewport on a phone, and it scrolls internally — the page behind it keeps its
 * own scroll position, which is what lets someone check a paragraph mid-answer
 * without losing the conversation.
 */
.ask__log {
  display: grid;
  gap: var(--sp-2);
  /* A third of a phone, at most: past that the card stops being a panel on the
     article and becomes a page over it. It scrolls internally instead. */
  max-height: min(34dvh, 18rem);
  padding-right: var(--sp-1);
  overflow-y: auto;
  overscroll-behavior: contain;
}

@media (min-width: 40rem) {
  .ask__log { gap: var(--sp-3); max-height: min(42dvh, 26rem); }
}

.ask__turn { display: flex; gap: var(--sp-2); }
.ask__turn--reader { justify-content: flex-end; }

.ask__bubble {
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A question is short and belongs to one person, so it is capped and hugs the
   right edge; an answer is prose and gets the whole column to be read in. */
.ask__turn--reader .ask__bubble {
  max-width: 42ch;
  background: var(--blue);
  color: #fff;
  border-bottom-right-radius: var(--r-sm);
}

.ask__turn--ai .ask__bubble {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-bottom-left-radius: var(--r-sm);
}

.ask__answer { display: grid; gap: var(--sp-2); min-width: 0; flex: 1; }

/*
 * "Read this next".
 *
 * Real links, styled as links: every entry is an article this page already
 * points to, so following one is the same move as clicking the related card
 * further up. Boxed rather than listed, because they interrupt a conversation
 * and should read as a hand-off, not as more of the answer.
 */
.ask__next {
  display: grid;
  gap: var(--sp-2);
  margin-top: var(--sp-1);
  padding: var(--sp-3);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--bg);
}

.ask__next-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--text-3);
}

.ask__next-item {
  display: grid;
  gap: 2px;
  padding: var(--sp-2);
  border-radius: var(--r-sm);
  color: var(--text);
}

.ask__next-item:hover { background: var(--surface); text-decoration: none; }

.ask__next-title {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  line-height: var(--lh-snug);
}

/* Two lines of summary: enough to choose by, not enough to read instead. */
.ask__next-summary {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* A refusal is styled as information, not as an error — it is a correct answer. */
.ask__turn--refused .ask__bubble {
  border-color: rgba(211, 163, 90, 0.35);
  background: rgba(211, 163, 90, 0.07);
}

/* Three dots, the universal "working". */
.ask__dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: var(--sp-3) var(--sp-3);
}

.ask__dots i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-3);
  animation: ask-dot 1.1s var(--ease) infinite;
}

.ask__dots i:nth-child(2) { animation-delay: 0.15s; }
.ask__dots i:nth-child(3) { animation-delay: 0.3s; }

@keyframes ask-dot {
  0%, 60%, 100% { opacity: 0.28; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-2px); }
}

/* ---------------------------------------------------------- the composer */

.ask__composer { display: grid; gap: var(--sp-2); }

/*
 * One field, the way a phone chat app draws it: the writing area and the send
 * button share a rounded surface, so the button reads as part of the box rather
 * than as a second control beside it. The button is small and the text area is
 * everything else — this panel is for typing.
 */
.ask__field {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--bg);
  transition: border-color var(--dur-fast) var(--ease);
}

.ask__field:focus-within { border-color: var(--blue); }

/*
 * One line to start with, growing as it is typed into.
 *
 * It used to open three rows tall, which on a phone reserved space for a
 * question nobody had written yet — and reserved it out of the article. Starting
 * at one line and growing means the card is only ever as tall as what is
 * actually in it.
 *
 * 16px on a phone is not a style choice: iOS Safari zooms the whole page when a
 * focused field is smaller, and the zoom does not come back on blur.
 */
.ask__box {
  min-height: 2.25rem;
  max-height: 6.5rem;
  padding: var(--sp-1) 0;
  border: 0;
  background: none;
  color: var(--text);
  font-size: 1rem;
  line-height: var(--lh-normal);
  resize: none;
  overflow-y: auto;
}

.ask__box:focus { outline: none; }
.ask__box::placeholder { color: var(--text-3); }

/*
 * 34px on a phone, 36px from `40rem` up.
 *
 * Deliberately under `--tap`: it sits INSIDE the writing field, and a 44px
 * circle in there reads as a button with a text box attached rather than a text
 * box with a send affordance. The field around it is 44px+ and the whole of it
 * is the touch surface, so the thumb target is honest even though the circle is
 * smaller than one.
 */
.ask__send {
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: var(--blue);
  color: #fff;
  transition: background var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}

@media (min-width: 40rem) {
  .ask__box { min-height: 2.5rem; max-height: 10rem; font-size: var(--fs-md); }
  .ask__send { width: 36px; height: 36px; }
}

.ask__send:hover:not([disabled]) { background: var(--blue-bright); }

/* Dimmed rather than hidden: the target a reader is aiming for stays put. */
.ask__send[disabled] { background: var(--surface-2); color: var(--text-3); }

.ask__error {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid rgba(224, 115, 107, 0.35);
  border-radius: var(--r-sm);
  background: rgba(224, 115, 107, 0.08);
  color: var(--danger);
  font-size: var(--fs-xs);
  line-height: var(--lh-normal);
}

/* -------------------------------------------------------------- the meter */

/* The one number left in the panel, and it is about the panel itself. */
.ask__foot {
  display: grid;
  gap: var(--sp-2);
  justify-items: center;
}

.ask__meter-label { color: var(--text-3); font-weight: var(--fw-semi); }

/*
 * The budget bar, made legible.
 *
 * It was a 3px hairline in a track the same colour as the card — technically
 * present, effectively invisible, so the one number the panel reports was the
 * one thing nobody saw. It is now 8px in a track with its own edge, and it
 * changes colour as it empties, so the state is readable before the sentence
 * under it is.
 */
.ask__meter {
  width: 100%;
  height: 8px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-pill);
  background: var(--bg);
  overflow: hidden;
}

.ask__meter-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--blue), var(--blue-bright));
  transition: width var(--dur-slow) var(--ease-out), background var(--dur) var(--ease);
}

/* Plenty left, running down, nearly gone. */
.ask__meter-fill[data-level="mid"] { background: linear-gradient(90deg, #b8862f, #d3a35a); }
.ask__meter-fill[data-level="low"] { background: linear-gradient(90deg, #b4483f, var(--danger)); }

/*
 * The character counter, under the field.
 *
 * Quiet until it matters: tertiary ink while there is room, amber in the last
 * 10%, and the danger colour once the cap is reached — so the limit is met as
 * information rather than as a keystroke that does nothing.
 */
.ask__count {
  justify-self: end;
  padding-right: var(--sp-1);
  color: var(--text-3);
  transition: color var(--dur-fast) var(--ease);
}

.ask__count[data-level="near"] { color: #d3a35a; }
.ask__count[data-level="full"] { color: var(--danger); font-weight: var(--fw-semi); }

/* On a wide screen the panel keeps the reading measure rather than stretching. */
@media (min-width: 60rem) {
  .ask__log { max-height: min(46dvh, 30rem); }
}

/*
 * In the preset gallery the card is a specimen, not a dock: there is no article
 * below it to stick to, and a sticky specimen would follow the reviewer down a
 * page of other specimens.
 */
.specimen__ask .ask {
  position: static;
  max-width: var(--w-reading);
  padding-top: 0;
}
