/* ==========================================================================
   The Ask room -- the luminous hollow.

   Everything shared comes from elsewhere: `styles.css` for the components
   -- the shell, the bar, the knob, the panel head, the pills -- and
   `themes.css` for what this room is made of. Nothing shared is redefined
   here (`npm run check` refuses a bare class selector that is also a
   component in styles.css).

   What is left is what only this room has: a bar you type in, a
   conversation, the work rows of the tools as a thread of lights, the files
   they make, and the light that travels round the edge while the model
   thinks.
   ========================================================================== */

/* --- the shell, while this room is talking ------------------------------------

   Wider than the bar, narrower than a document: a conversation is read
   down a column, and past about 760px a line of prose stops helping. */
.stage[data-state="ask-chat"] .shell { width: min(100%, 760px); }

/* At rest the island breathes: the room's own glow at its edge, coming and
   going over seven seconds. A shadow, so nothing moves and nothing is
   repainted but the edge. Under reduced motion it rests at its middle. */
.stage[data-state="ask-idle"] .shell {
  animation: ask-breathe 7s var(--ease) infinite;
}
@keyframes ask-breathe {
  0%, 100% { box-shadow: 0 0 12px -2px rgba(79, 168, 140, 0.16); }
  50%      { box-shadow: 0 0 18px -2px rgba(79, 168, 140, 0.30); }
}

/* --- the bar: a field to type in, a plus for files, the knob to send ---------- */

/* One bar, at rest and at the foot of the conversation (faces.js): the
   same element, so it cannot look different in one place. */
.ask-bar { gap: var(--s2); padding-left: var(--s2); padding-right: var(--s2); cursor: text; }

.ask-field {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}
/* What is written is set like the line that types itself -- the room's
   face for it, its size -- but in the visitor's own capitals. */
.ask-input {
  width: 100%;
  resize: none;
  font-family: var(--rest-type-font);
  font-size: var(--rest-type-size);
  letter-spacing: var(--rest-type-track);
  line-height: var(--lh-prose);
  /* A field that grows with what is typed (room.js, fit), from one line. */
  max-height: calc(6 * 1.55em + var(--s4));
  scrollbar-width: none;
}
.ask-input::-webkit-scrollbar { display: none; }
/* The line that types itself sits in the empty field whenever it is empty
   -- with the caret in it too -- and gives way to the first letter written.
   The field's own caret waits until then: the line has its own, and two
   blinking in one bar would be one too many. */
.ask-type {
  position: absolute;
  inset: 0 0 0 0;
  display: flex;
  align-items: center;
  pointer-events: none;
  transition: opacity var(--dur-state) var(--ease);
}
.ask-bar[data-typed] .ask-type { opacity: 0; }
.ask-bar:not([data-typed]) .ask-input { caret-color: transparent; }

.ask-add {
  flex: none;
  position: relative;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border: var(--hairline) solid var(--film-rule);
  border-radius: var(--radius-control);
  background: none;
  color: var(--film-ink-2);
  cursor: pointer;
  transition: background-color var(--dur-state) var(--ease),
              color var(--dur-state) var(--ease),
              border-color var(--dur-state) var(--ease),
              box-shadow var(--dur-state) var(--ease);
}
.ask-add:hover { background: var(--film-raise); color: var(--film-ink); border-color: var(--film-ink-3); box-shadow: var(--react-lift); }
.ask-add:active { transform: scale(var(--react-press)); }
.ask-add svg, .ask-chip__drop svg {
  width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* The knob sends, and while anything is being made it stops it: the same
   control, in the same place, with its square showing instead of its
   arrow. */
.ask-knob .ask-knob__stop { display: none; fill: currentColor; stroke: none; }
.ask-knob[data-busy] > svg:first-child { display: none; }
.ask-knob[data-busy] .ask-knob__stop { display: block; }

/* --- files waiting to go with the next message -------------------------------- */

/* On the resting face the files go under the bar, so the island grows
   downward to show them (the building's grow) and never cuts the bar. At
   the foot of the conversation they sit above the field, as a message's
   files do. */
.ask-tray { padding: var(--s2) var(--s2) 0; }
.face[data-face="ask-chat"] .ask-tray { padding: 0 var(--s2) var(--s2); }
.ask-chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}
.ask-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  max-width: 100%;
  min-height: 36px;
  padding: 0 var(--s1) 0 var(--s3);
  border-radius: var(--radius-control);
  background: var(--film-raise);
  font-size: var(--t-sm);
}
.ask-chip__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--film-ink); }
.ask-chip__size { color: var(--film-ink-3); font-size: var(--t-xs); white-space: nowrap; }
.ask-chip__drop {
  position: relative;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border: 0; border-radius: var(--radius-control);
  background: none; color: var(--film-ink-3);
  cursor: pointer;
  transition: background-color var(--dur-state) var(--ease), color var(--dur-state) var(--ease);
}
.ask-chip__drop:hover { background: var(--film-raise-hi); color: var(--film-ink); }
.ask-chip__drop svg { width: 14px; height: 14px; }

/* --- the conversation ---------------------------------------------------------- */

/* The conversation is a hollow of one size: as tall as the window leaves
   room for, from the first question on. Around it are 233px: the top bar
   (92), the stage's margins (24 and 48), the foot of the page (67) and the
   shell's two hairlines, measured at 620 to 1000px tall, so the page never
   scrolls on a computer. Its words scroll inside it, so the composer stays
   where it is however much is said or added. The small viewport (`svh`):
   a phone's address bar coming and going while the page moves must not
   change the hollow's size.

   (It used to grow with what was said. The building's grow holds the panel
   at its old height while what was added is already laid out inside it.
   That suits a face that grows down from a bar at its top, but not one whose
   bar is at its foot: the composer was cut by the panel's edge until the
   panel caught up, in both browsers, and anything taken away left a hole
   under it.)

   The rule covers the face while it leaves too, so it keeps its shape as it
   fades instead of springing open to its whole conversation. */
/* Positioned for the light round its edge -- but never while it leaves:
   the building takes a leaving face out of the flow (`.face.is-leaving`,
   absolute, as specific as this and earlier in the cascade), and this rule
   used to win, so the face going away still stood in the shell. The shell
   then measured the resting bar as 710px tall, kept its height while its
   corners rounded, and snapped to the bar at the end. */
.face[data-face="ask-chat"]:not(.is-leaving) { position: relative; }
.face[data-face="ask-chat"]:is(.is-active, .is-leaving) {
  display: flex;
  flex-direction: column;
  height: clamp(360px, calc(100vh - 236px), 700px);
  height: clamp(360px, calc(100svh - 236px), 700px);
}
/* While the panel opens to it (the building's morph sets the shell's height
   as it grows), the face is held to the shell's height, so that only the
   conversation gives way: the hollow opens between the head and the
   composer instead of the composer waiting below the edge until the end.
   At rest the shell's height is its content's, the percentages resolve to
   nothing, and the face is its own height above. The face leaving is left
   out on purpose: held to a shell already closing to the bar, it would
   crush at once, and it is only fading. */
.stage[data-state="ask-chat"] .shell__faces { height: 100%; }
.face[data-face="ask-chat"].is-active { max-height: 100%; }
.ask-head { flex: none; align-items: center; margin-bottom: var(--s4); }
.ask-head .panel__top { flex: 1; }
.ask-head__tools { display: flex; gap: var(--s2); flex: none; }
/* Where the bar stands while the conversation shows: as it stands in the
   island at rest, 12px from the panel's sides and foot (the resting face's
   padding), with a hairline above it where the island's edge would be. So
   the bar does not move in from somewhere else when the hollow opens -- it
   is the island's bar, and the panel grows up from around it. */
.ask-foot {
  flex: none;
  margin: var(--s3) calc(var(--s3) - clamp(var(--s4), 3vw, var(--s6))) calc(var(--s3) - var(--s5));
  padding-top: var(--s3);
  border-top: var(--hairline) solid var(--shell-edge, var(--film-rule));
}
/* The bar at the foot answers the pointer as it does at rest. */
.face[data-face="ask-chat"] .ask-bar:hover { background: var(--react-bar-hover); }
.face[data-face="ask-chat"] .ask-bar { --sheen-reach: 300px; }

/* While a face changes, the shell's height is set as it grows or closes
   (shell.js), and the face showing is held to its foot: the hollow opens up
   from the bar, and closes down onto it on the way back, and the bar rides
   the bottom edge both ways. Only then: the island's own grow at rest (the
   files under the bar) still shows from below. */
.stage:is([data-state="ask-idle"], [data-state="ask-chat"]) .shell.is-morphing > .shell__faces {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
/* And inside the conversation, while it is still shorter than its head and
   its bar, what does not fit goes out at the top: the bar stays in sight. */
.shell.is-morphing .face[data-face="ask-chat"].is-active { justify-content: flex-end; }

.ask-log {
  flex: 1 1 0;
  min-height: 0;
  list-style: none;
  margin: 0 calc(-1 * var(--s2));
  padding: var(--s1) var(--s2) var(--s4);
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.ask-log::-webkit-scrollbar { display: none; }
/* The conversation rises from the composer, as it will look once it fills
   the hollow: the first words sit just above the field, and the room left
   over is above them. A margin rather than `justify-content: flex-end`,
   which would push an overflowing conversation out of reach of the scroll
   at its top. */
.ask-log > :first-child { margin-top: auto; }

/* The visitor's words: a pebble of the cave's raised glass, to the right,
   quieter than the answer they asked for. */
.ask-turn--you { align-self: flex-end; max-width: min(82%, 52ch); }
.ask-you {
  padding: var(--s2) var(--s4);
  border-radius: 18px;
  background: var(--film-raise);
  color: var(--film-ink);
}
.ask-you p, .ask-prose p { margin: 0; }
.ask-you p + p, .ask-prose p + p { margin-top: var(--s2); }
.ask-you__files {
  margin: var(--s1) var(--s2) 0;
  font-size: var(--t-xs);
  color: var(--film-ink-3);
  text-align: right;
  overflow-wrap: anywhere;
}

/* The answer: prose, on the dark, at the measure prose wants. */
.ask-turn--model { max-width: 62ch; }
.ask-prose { color: var(--film-ink); overflow-wrap: anywhere; }
.ask-prose ul, .ask-prose ol { margin: var(--s2) 0 0; padding-left: var(--s5); }
.ask-prose li + li { margin-top: var(--s1); }
.ask-prose code { font-family: var(--font-mono); font-size: 0.92em; color: var(--film-ink-2); }
.ask-prose:empty { display: none; }
/* Before the first word or the first step: one point of the room's light,
   breathing, where the answer will be -- the model is reading, or writing a
   call it has not finished yet. */
.ask-wait { display: none; }
.ask-turn--model[data-state="answering"] .ask-wait {
  display: block;
  width: 8px; height: 8px;
  margin: var(--s2) 0;
  border-radius: 50%;
  background: var(--signal);
  box-shadow: 0 0 8px rgba(79, 168, 140, 0.55);
  animation: ask-glow 1.6s var(--ease) infinite;
}
.ask-turn--model[data-state="answering"]:has(.ask-prose:not(:empty)) .ask-wait,
.ask-turn--model[data-state="answering"]:has(.ask-steps:not([hidden])) .ask-wait { display: none; }

/* The newest words as they arrive carry the room's light, and fall back
   into the ink as more come in (room.js, lightTail). */
.ask-lit { color: color-mix(in oklab, var(--signal) 70%, var(--film-ink)); }

.ask-note { margin-top: var(--s2); font-size: var(--t-xs); color: var(--film-ink-3); }
.ask-turn--model[data-state="failed"] .ask-note,
.ask-turn--model[data-state="refused"] .ask-note { color: var(--film-bad); }

/* --- what the tools did: a thread of lights ------------------------------------

   Each step a point of light on one silk thread hanging down the answer's
   left edge -- the hollow's own image. A point that is working breathes;
   one that is done holds its light; one that failed has gone the colour the
   building uses for failure, and says why beside it. */
.ask-steps {
  list-style: none;
  margin: var(--s3) 0 0;
  padding: 0 0 0 var(--s4);
  border-left: var(--hairline) solid rgba(79, 168, 140, 0.28);
  display: grid;
  gap: var(--s2);
}
.ask-step {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--s3);
  font-size: var(--t-sm);
  color: var(--film-ink-2);
}
.ask-step__light {
  position: absolute;
  left: calc(-1 * var(--s4) - 4.5px);
  top: 50%;
  width: 8px; height: 8px;
  margin-top: -4px;
  border-radius: 50%;
  background: var(--signal);
  box-shadow: 0 0 8px rgba(79, 168, 140, 0.55);
}
.ask-step[data-state="working"] .ask-step__light { animation: ask-glow 1.6s var(--ease) infinite; }
.ask-step[data-state="failed"] .ask-step__light { background: var(--film-bad); box-shadow: none; }
.ask-step[data-state="failed"] .ask-step__words { color: var(--film-bad); }
@keyframes ask-glow {
  0%, 100% { opacity: 1; box-shadow: 0 0 8px rgba(79, 168, 140, 0.55); }
  50%      { opacity: 0.55; box-shadow: 0 0 2px rgba(79, 168, 140, 0.2); }
}
.ask-step__words { overflow-wrap: anywhere; }
.ask-step__track {
  width: 72px; height: 3px;
  border-radius: var(--radius-control);
  background: var(--film-raise);
  overflow: hidden;
}
.ask-step__fill {
  display: block;
  height: 100%;
  background: var(--signal);
  transform-origin: left center;
  transform: scaleX(var(--done, 0));
  transition: transform var(--dur-state) var(--ease);
}

/* --- what was made --------------------------------------------------------------

   A file is a thing in the conversation, not a link in a sentence: its
   kind, its name and size in the mono the building keeps for those, and
   what can be done with it. */
.ask-cards { margin-top: var(--s3); display: grid; gap: var(--s2); }
.ask-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--s3);
  row-gap: var(--s2);
  padding: var(--s2) var(--s2) var(--s2) var(--s3);
  border-radius: 16px;
  background: var(--film-raise);
}
.ask-card__kind {
  min-width: 44px;
  padding: var(--s1) var(--s2);
  border-radius: 8px;
  background: var(--film-well);
  color: var(--signal);
  font-size: var(--t-xs);
  text-align: center;
}
.ask-card__name { display: grid; min-width: 0; }
.ask-card__file { color: var(--film-ink); font-size: var(--t-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ask-card__meta { color: var(--film-ink-3); font-size: var(--t-xs); }
.ask-card__acts { display: flex; gap: var(--s2); flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 520px) {
  .ask-card { grid-template-columns: auto minmax(0, 1fr); }
  .ask-card__acts, .ask-card__go { grid-column: 1 / -1; justify-content: flex-start; }
}

/* --- the model's offer ----------------------------------------------------------- */

.ask-offer__why { color: var(--film-ink-2); }
.ask-offer__picks { margin-top: var(--s3); display: flex; flex-wrap: wrap; gap: var(--s2); }
.ask-fetch { margin-top: var(--s3); display: grid; gap: var(--s2); max-width: 52ch; }
.ask-fetch__track {
  height: 3px;
  border-radius: var(--radius-control);
  background: var(--film-raise);
  overflow: hidden;
}
.ask-fetch__fill {
  height: 100%;
  background: var(--signal);
  transform-origin: left center;
  transform: scaleX(var(--done, 0));
  transition: transform var(--dur-face) var(--ease);
}
.ask-offer[data-state="loading"] .ask-fetch__fill { animation: ask-glow 1.6s var(--ease) infinite; }
.ask-fetch__words { font-size: var(--t-xs); color: var(--film-ink-3); }
.ask-offer[data-state="failed"] .ask-fetch__words { color: var(--film-bad); }

/* --- thought travelling along the edge -------------------------------------------

   While the model answers or a tool works, one point of the room's light
   goes round the panel's edge, slowly: PLATFORM.md's "thinking travels as
   light along the edges". A ring masked to the border, as the building's
   refraction is, in the room's own colour and nothing else. Under reduced
   motion the edge simply holds a little more light. */
@property --ask-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}
.ask-light {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--radius-shell-open) - 1px);
  padding: 1.5px;
  pointer-events: none;
  opacity: 0;
  background: conic-gradient(from var(--ask-angle),
    rgba(79, 168, 140, 0) 0deg,
    rgba(79, 168, 140, 0) 250deg,
    rgba(127, 214, 192, 0.85) 330deg,
    rgba(206, 245, 232, 0.95) 352deg,
    rgba(79, 168, 140, 0) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  transition: opacity var(--dur-face) var(--ease);
}
.face[data-face="ask-chat"][data-working] .ask-light {
  opacity: 1;
  animation: ask-travel 3.2s linear infinite;
}
@keyframes ask-travel { to { --ask-angle: 360deg; } }
.stage[data-state="ask-chat"] .shell:has(.face.is-active[data-working]) { box-shadow: var(--react-edge-moving); }

@media (prefers-reduced-motion: reduce) {
  .stage[data-state="ask-idle"] .shell { animation: none; box-shadow: 0 0 15px -2px rgba(79, 168, 140, 0.23); }
  .face[data-face="ask-chat"][data-working] .ask-light { animation: none; opacity: 0; }
  .ask-step[data-state="working"] .ask-step__light,
  .ask-turn--model[data-state="answering"] .ask-wait,
  .ask-offer[data-state="loading"] .ask-fetch__fill { animation: none; }
}

/* --- a finger instead of a pointer ----------------------------------------------- */
@media (pointer: coarse) {
  .ask-add { width: var(--hit, 44px); height: var(--hit, 44px); }
  .ask-chip__drop::after { content: ""; position: absolute; inset: var(--hit-reach, -8px); }
}

/* --- a phone ------------------------------------------------------------------------ */
@media (max-width: 560px) {
  /* The model's name and where it runs keep the width beside the way back;
     Model and New go to a row of their own under them. */
  .ask-head { flex-wrap: wrap; row-gap: var(--s2); }
  .ask-head .panel__top { flex: 1 1 calc(100% - 44px - var(--s4)); }
  .ask-head__tools { flex: 1 1 100%; justify-content: flex-end; }
  .ask-turn--you { max-width: 90%; }
  /* The top bar is two rows here (126px) and the shell sits under it at
     150px: the hollow ends 80px above the window's foot, the composer
     always in sight, and the page's own foot a short scroll below it. */
  .face[data-face="ask-chat"]:is(.is-active, .is-leaving) {
    height: clamp(340px, calc(100vh - 230px), 640px);
    height: clamp(340px, calc(100svh - 230px), 640px);
  }
}
