/* ============================================================
   Virtuoso — "Ask your data anything" animated canvas
   Built from the storyboard in Figma node 3147:9278 (the revised
   frame, file PzdONwyHDXl91xXwLi9UJW). Element geometry is 1:1 with
   the three animated panels; the choreography between them is the
   brief. The copy-and-CTA panel at the left of that Figma frame is
   the section's left column and is NOT part of this canvas.

   All rules are scoped to #virtuoso-ask: nothing leaks into the
   host page. No Tailwind, no external requests, no CDN calls.

   FONT: Mulish for the chat and the chart title, Inter for the
   chart labels — both as drawn. Either confirm the host page loads
   them, or self-host and uncomment below. Do NOT ship a CDN call.

   BACKGROUND: the canvas is transparent by design. The host
   section provides the dark backdrop it sits on.

   SIZING: laid out in a 600 x 600 design grid via --u, so it
   scales as one piece at any width. Needs container query support.

   LAYOUT: two groups, not one column. The QUESTION group is the prompt
   window with the querying line pinned below it; the RESULT group is the
   answer with the chart below that. They have to be separate, because in
   the last panel the question leaves while the answer stays behind. But
   within the result group the answer and the chart share one position
   and one opacity, so they can never drift apart or fade separately.
   ============================================================ */


#virtuoso-ask {
  --ask-paper: #F9F9F9;

  /* The window's gradient. Derived from the node's gradientStops and
     gradientTransform, NOT from Figma's CSS export — that export drops the
     minus sign on the first stop (it emits 13.62% for what is really
     -13.620%), which compresses the ramp and moves the cyan corner.
     One angle serves both window states. The paint is identical in the file;
     only the box aspect changes the CSS angle (126.54deg expanded, 139.89deg
     shrunk), and re-deriving it mid-animation would slide the ramp while the
     box resizes. The expanded angle is the one on screen longest. */
  --ask-window-grad: linear-gradient(126.5364deg,
                     var(--virtuoso-tertiary) -13.620%, var(--virtuoso-secondary) 22.581%, var(--virtuoso-primary) 101.371%);

  display: block;
  width: 100%;
}
#virtuoso-ask *, #virtuoso-ask *::before, #virtuoso-ask *::after { box-sizing: border-box; }

/* the canvas is a picture, so its heading is for assistive tech only */
#virtuoso-ask .ask-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The shell owns the size and is the query container. A separate element is
   required: an element cannot use container query units resolved against
   itself. 1:1 as briefed. */
#virtuoso-ask .ask-shell {
  container-type: inline-size;
  width: 100%;
  aspect-ratio: 1 / 1;
}

/* 1 design pixel. 600 design px == 100cqw, so every measurement below is the
   number from the storyboard.

   The defaults below are the loop's t=0 — the third panel, held: the answer
   and the finished chart, centred as a pair, the question already gone and the
   donut fully drawn. That is deliberate. With JavaScript disabled the canvas
   renders that panel rather than an empty box, and because it is genuinely
   t=0 there is nothing to jump when the engine takes over.

   --res-y here is the pair's centred position for a two-line answer, and
   --chart-slide 0 puts the chart in its resting slot. The engine recomputes
   the position from the answer's measured height. */
#virtuoso-ask .ask-stage {
  --u: calc(100cqw / 600);

  /* the question side: window plus querying line, which move and leave together */
  --ask-y: 349.5;
  --ask-o: 0;
  --win-h: 141;
  --win-pt: 15;
  --ctrl-o: 1;
  --knob-x: 0;
  --track-o: 0.28;
  --send-s: 1;
  --send-glow: 0;
  --q-o: 0;

  /* The result group: the answer with the chart 30 below it. One position and
     one opacity for the pair, plus the chart's own slide offset inside the
     group, which is what lets it rise into its slot from below. */
  --res-y: 97.4;
  --res-o: 1;
  --ans-h: 42;
  --chart-slide: 0;

  /* --chart-s is the one number to turn if the box wants to be a different
     size: it scales the whole panel, internals and type included, about its
     top centre, so nothing else has to be re-derived. 0.85 is the 15%
     reduction asked for on review. */
  --chart-s: 0.85;

  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  /* The gradient opacity mask the brief asks for. It softens both ends of the
     conveyor: the chart rising in from below and the question leaving through
     the top. Stops are set clear of where anything rests. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 6%, #000 92%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 6%, #000 92%, transparent 100%);
}

/* both groups are 500 wide in a 600 stage */
#virtuoso-ask .ask-window,
#virtuoso-ask .ask-result {
  position: absolute;
  left: calc(50 * var(--u));
  top: 0;
  width: calc(500 * var(--u));
}

/* ═══════════════════════════════════════════════════════════════════════
     THE QUESTION  ·  the prompt window, and the querying line beneath it
   ═══════════════════════════════════════════════════════════════════════ */

#virtuoso-ask .ask-window {
  height: calc(var(--win-h) * var(--u));
  /* Vertical padding animates with the shrink so the prompt stays optically
     centred in the collapsed box, as the file draws it (15 expanded, 19.6
     shrunk). Horizontal padding is deliberately frozen at 30: changing it
     would re-wrap the prompt mid-animation. */
  padding: calc(var(--win-pt) * var(--u)) calc(30 * var(--u));
  border: calc(2 * var(--u)) solid rgb(255 255 255 / 0.2);
  border-radius: calc(8 * var(--u));
  overflow: clip;
  display: flex;
  flex-direction: column;
  gap: calc(15 * var(--u));
  /* The solid purple sits under the gradient, as in the file.
     background-origin MUST be border-box. It defaults to padding-box while
     background-clip defaults to border-box, so the gradient gets sized to the
     padding box and then tiled outward into the 2px border — which paints the
     ramp's purple end on the blue edge and its cyan start on the purple edge.
     Sizing it to the border box makes one continuous ramp with no repeat. */
  background: var(--ask-window-grad), var(--virtuoso-primary);
  background-origin: border-box;
  background-repeat: no-repeat;
  transform: translateY(calc(var(--ask-y) * var(--u)));
  opacity: var(--ask-o);
  will-change: transform, opacity;
}

/* The typed prompt. This is the input copy and it keeps the file's 16px.
   Height reserved for two lines so the window never reflows while the text
   arrives, and the width never changes either. */
#virtuoso-ask .ask-q {
  margin: 0;
  flex: 0 0 auto;
  height: calc(44.8 * var(--u));
  overflow: hidden;
  font-size: calc(16 * var(--u));
  /* Regular, not the SemiBold the file specifies: at this size the SemiBold
     read heavy against the gradient. */
  font-weight: 400;
  line-height: 1.4;
  color: #FFFFFF;
}

/* The querying line is pinned a fixed 30 below the window's bottom edge, so it
   rides the window's every move — including off the top of the stage — without
   needing a track of its own. --q-o is its own fade-in, multiplied by the
   question side's opacity so the exit takes it too. */
#virtuoso-ask .ask-querying {
  position: absolute;
  left: calc(50 * var(--u));
  top: 0;
  height: calc(26 * var(--u));
  display: flex;
  align-items: center;
  gap: calc(11 * var(--u));
  transform: translateY(calc((var(--ask-y) + var(--win-h) + 30) * var(--u)));
  opacity: calc(var(--ask-o) * var(--q-o));
  will-change: transform, opacity;
}
#virtuoso-ask .ask-sparkle {
  flex: 0 0 auto;
  width: calc(26 * var(--u));
  height: calc(26 * var(--u));
  display: block;
}

/* ---- the composer controls ---- */
#virtuoso-ask .ask-controls {
  flex: 0 0 auto;
  width: 100%;
  height: calc(48 * var(--u));
  display: flex;
  align-items: center;
  justify-content: space-between;
  opacity: var(--ctrl-o);
}
#virtuoso-ask .ask-controls-left {
  display: flex;
  align-items: flex-start;
  gap: calc(10 * var(--u));
}
#virtuoso-ask .ask-plus,
#virtuoso-ask .ask-pill {
  background: rgb(217 217 217 / 0.22);
  border: calc(1 * var(--u)) solid rgb(255 255 255 / 0.18);
}
#virtuoso-ask .ask-plus {
  width: calc(44 * var(--u));
  height: calc(45 * var(--u));
  border-radius: calc(5 * var(--u));
  display: flex;
  align-items: center;
  justify-content: center;
  padding-bottom: calc(5 * var(--u));
  font-size: calc(30 * var(--u));
  font-weight: 400;
  line-height: 1.4;
  color: #FFFFFF;
}
#virtuoso-ask .ask-pill {
  height: calc(44.918 * var(--u));
  border-radius: calc(8 * var(--u));
  display: flex;
  align-items: center;
  gap: calc(21 * var(--u));
  padding: calc(4 * var(--u)) calc(9 * var(--u));
  font-size: calc(14 * var(--u));
  font-weight: 400;
  line-height: 1.4;
  color: #FFFFFF;
  white-space: nowrap;
}

/* The toggle. Figma only draws the ON state, so OFF is inferred: the same
   white track dropped to 28% with the knob at the left. The engine drives
   --track-o and --knob-x, so the flip is one continuous move. */
#virtuoso-ask .ask-toggle {
  flex: 0 0 auto;
  width: calc(36 * var(--u));
  height: calc(17 * var(--u));
  border-radius: calc(26 * var(--u));
  border: calc(1 * var(--u)) solid #FFFFFF;
  padding: calc(2 * var(--u));
  background: rgb(255 255 255 / var(--track-o));
  display: flex;
  align-items: center;
}
#virtuoso-ask .ask-knob {
  width: calc(12 * var(--u));
  height: calc(12 * var(--u));
  display: block;
  transform: translateX(calc(var(--knob-x) * var(--u)));
}

/* ---- send button ---- */
#virtuoso-ask .ask-send {
  position: relative;
  flex: 0 0 auto;
  width: calc(48 * var(--u));
  height: calc(48 * var(--u));
  transform: scale(var(--send-s));
  filter: drop-shadow(0 0 calc(3.35 * var(--u)) rgb(255 255 255 / calc(0.9 + 0.1 * var(--send-glow))))
          drop-shadow(0 0 calc(10 * var(--u)) rgb(255 255 255 / calc(0.55 * var(--send-glow))));
}
#virtuoso-ask .ask-send-ring {
  position: absolute;
  left: calc(4.8 * var(--u));
  top: calc(5 * var(--u));
  width: calc(38.4 * var(--u));
  height: calc(38.4 * var(--u));
  display: block;
}
#virtuoso-ask .ask-send-arrow {
  position: absolute;
  left: calc(17.26 * var(--u));
  top: calc(16.836 * var(--u));
  width: calc(13.48 * var(--u));
  height: calc(14.7279 * var(--u));
  display: block;
}

/* ═══════════════════════════════════════════════════════════════════════
     THE ANSWER  ·  on its own track, because it outlives the question
   ═══════════════════════════════════════════════════════════════════════ */

/* THE RESULT GROUP owns the position and the opacity for both the answer and
   the chart. That is the whole point: they arrive, rest and leave as one thing,
   and there is no way for one to fade or move without the other. */
#virtuoso-ask .ask-result {
  transform: translateY(calc(var(--res-y) * var(--u)));
  opacity: var(--res-o);
  will-change: transform, opacity;
}

/* The answer is the output copy, at 15px rather than the file's 14px per
   review. It sits in flow at the top of the group, so the group's position IS
   the answer's position.

   --ans-h is measured at runtime rather than hard-coded. The copy is
   placeholder and at 15px its line count in a 500-wide column is not
   something to guess at — and both centred compositions are derived from it,
   so a wrong guess would put the whole frame off centre. */
#virtuoso-ask .ask-answer {
  position: relative;
  margin: 0;
  height: calc(var(--ans-h) * var(--u));
  overflow: hidden;
  font-size: calc(15 * var(--u));
  font-weight: 400;
  line-height: 1.4;
  color: var(--ask-paper);
}
/* the status line is output copy too, so it tracks the answer's size */
#virtuoso-ask .ask-querying span {
  font-size: calc(15 * var(--u));
  font-weight: 400;
  line-height: 1.4;
  color: var(--ask-paper);
  white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════
     THE RESULT  ·  the chart panel
   ═══════════════════════════════════════════════════════════════════════ */

/* The border is a 40%-opacity gradient stroke in the file, not the flat purple
   the CSS export reports. A border cannot hold a gradient, so the translucent
   black fill is painted into the padding box and the gradient into the border
   box. Its first stop is negative too — derived, not exported.

   The panel is drawn at its full 500 x 392 and then scaled by --chart-s about
   its top centre, so its top edge stays put and the horizontal centring holds
   at any scale.

   Its resting slot is a fixed 30 below the answer, INSIDE the group — so that
   gap is structural, not animated. --chart-slide then lifts it from off-stage
   below into that slot. The gap between the answer and the chart is therefore
   always 30 + the slide, which means the chart can close on the answer but can
   never reach or cover it. */
#virtuoso-ask .ask-chart {
  position: absolute;
  left: 0;
  top: calc((var(--ans-h) + 30) * var(--u));
  width: calc(500 * var(--u));
  height: calc(392 * var(--u));
  border: calc(1 * var(--u)) solid transparent;
  border-radius: calc(8 * var(--u));
  background:
    linear-gradient(rgb(0 0 0 / 0.25), rgb(0 0 0 / 0.25)) padding-box,
    linear-gradient(-40.5921deg,
      rgb(154 28 228 / 0.4) -0.505%,
      rgb(0 114 206 / 0.4) 49.495%,
      rgb(9 216 255 / 0.4) 99.495%) border-box;
  transform: translateY(calc(var(--chart-slide) * var(--u))) scale(var(--chart-s));
  transform-origin: top center;
  will-change: transform;
}
#virtuoso-ask .ask-chart-title {
  position: absolute;
  left: 50%;
  top: calc(32.5 * var(--u));
  transform: translateX(-50%);
  margin: 0;
  font-size: calc(15 * var(--u));
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: calc(-0.165 * var(--u));
  text-align: center;
  white-space: nowrap;
  color: var(--ask-paper);
  opacity: 0.75;
}
#virtuoso-ask .ask-chart-rule {
  position: absolute;
  left: 50%;
  top: calc(60.5 * var(--u));
  transform: translateX(-50%);
  width: calc(223 * var(--u));
  height: calc(1 * var(--u));
  opacity: 0.75;
  background: linear-gradient(90deg, var(--virtuoso-tertiary) 0%, var(--virtuoso-secondary) 52.404%, var(--virtuoso-primary) 100%);
}
#virtuoso-ask .ask-donut {
  position: absolute;
  left: calc(136 * var(--u));
  top: calc(113.5 * var(--u));
  width: calc(230 * var(--u));
  height: calc(230 * var(--u));
  display: block;
}

/* THE FAN. Each of the six wedges swings into place about the donut's centre
   and fades up, one after another clockwise from 12 o'clock. --p is the
   segment's own progress, written by the engine; the fallback of 1 means the
   chart is fully drawn with no JavaScript and under reduced motion.
   transform-box: view-box makes transform-origin resolve against the SVG
   viewport (0 0 230 230) rather than each path's own tight bounding box, which
   is what lets all six rotate about the same point. */
#virtuoso-ask .ask-seg {
  opacity: var(--p, 1);
  transform-box: view-box;
  transform-origin: 115px 115px;
  transform: rotate(calc((1 - var(--p, 1)) * -11deg));
}

/* the six leader lines, as plain SVG lines rather than Figma's rotate/skew
   wrappers — endpoints derived from each wrapper's bounding box */
#virtuoso-ask .ask-leaders {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0.75;
}
/* Each leader line carries the --p of the slice it points at. The parent svg's
   0.75 and this multiply, so the resting look is unchanged. */
#virtuoso-ask .ask-leaders line { opacity: var(--p, 1); }

/* --x / --y come from the markup: six labels need one rule, not six */
#virtuoso-ask .ask-label {
  position: absolute;
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u));
  margin: 0;
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: calc(12 * var(--u));
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: calc(-0.132 * var(--u));
  white-space: nowrap;
  color: var(--ask-paper);
  opacity: calc(0.75 * var(--p, 1));
}
#virtuoso-ask .ask-label--mid { transform: translateX(-50%); text-align: center; }
#virtuoso-ask .ask-label i { font-style: normal; font-weight: 400; }

/* ---- reduced motion: hold the resting state, which is the third storyboard
        panel — the answer and the finished chart, no movement, no typing ---- */
@media (prefers-reduced-motion: reduce) {
  #virtuoso-ask .ask-stage {
    --ask-o: 0; --q-o: 0;
    --res-y: 97.4; --res-o: 1; --ans-h: 42; --chart-slide: 0;
  }
}
